什么是Task?
描述
- Task出現(xiàn)之前,微軟的多線(xiàn)程處理方式有:Thread→ThreadPool→委托的異步調(diào)用,雖然可以滿(mǎn)足基本業(yè)務(wù)場(chǎng)景,但它們?cè)诙鄠€(gè)線(xiàn)程的等待處理方面、資源占用方面、延續(xù)和阻塞方面都顯得比較笨拙,在面對(duì)復(fù)雜的業(yè)務(wù)場(chǎng)景下,顯得有點(diǎn)捉襟見(jiàn)肘
- Task是微軟在.Net 4.0時(shí)代推出來(lái)的,也是微軟極力推薦的一種多線(xiàn)程的處理方式,Task看起來(lái)像一個(gè)Thread,實(shí)際上,它是在ThreadPool的基礎(chǔ)上進(jìn)行的封裝
- Task的控制和擴(kuò)展性很強(qiáng),在線(xiàn)程的延續(xù)、阻塞、取消、超時(shí)等方面遠(yuǎn)勝于Thread和ThreadPool
- Task可以簡(jiǎn)單看作相當(dāng)于Thead+TheadPool,其性能比直接使用Thread要更好,在工作中更多的是使用Task來(lái)處理多線(xiàn)程任務(wù)
任務(wù)Task和線(xiàn)程Thread的區(qū)別
- Task是建立在Thread之上的,最終其實(shí)還是由Thread去執(zhí)行,它們都是在 System.Threading 命名空間下的
- Task跟Thread并不是一對(duì)一的關(guān)系。比如說(shuō)開(kāi)啟10個(gè)任務(wù)并不一定會(huì)開(kāi)啟10個(gè)線(xiàn)程,因?yàn)槭褂肨ask開(kāi)啟新任務(wù)時(shí),是從線(xiàn)程池中調(diào)用線(xiàn)程,這點(diǎn)與ThreadPool.QueueUserWorkItem類(lèi)似
Task的使用
創(chuàng)建Task的三種方式
- 方式一:通過(guò)創(chuàng)建Task對(duì)象后調(diào)用其 Start()函數(shù)
- 方式二:調(diào)用Task的靜態(tài)方法Run()
- 方式三:通過(guò)Task工廠(chǎng),新建一個(gè)線(xiàn)程
// 方式一,通過(guò)Start
Task t1 = new Task(() => { Console.WriteLine("我是Task方式一"); });
t1.Start();
// 方式二,通過(guò)Run
Task t2= Task.Run(()=>{Console.WriteLine("我是Task方式二"); });
// 方式三,通過(guò)工廠(chǎng)
Task t3= Task.Factory.StartNew(()=>{Console.WriteLine("我是Task方式三"); });
帶返回值與不帶返回值的Task
static void Main()
{
// 沒(méi)有返回參數(shù)
Task t1 = new Task(() => { Console.WriteLine("我是Task沒(méi)有返回參數(shù)"); });
t1.Start();
// 有返回參數(shù)
Task<int> t2 = new Task<int>(() => { return 1+1; });
t2.Start();
int result = t2.Result;
Console.WriteLine(result);
}
輸出結(jié)果
我是Task沒(méi)有返回參數(shù)
2
一次性建立多個(gè)任務(wù)場(chǎng)景
static void test1()
{
Task[] taskArray = new Task[10];
for (int i = 0; i < 10; i++)
{
int bb = i;
Task t = Task.Run(() => { Console.WriteLine("任務(wù)ID:{0}, 結(jié)果:{1}",Thread.CurrentThread.ManagedThreadId, bb); });
taskArray[i] = t;
}
// 等待所有任務(wù)完成
Task.WaitAll(taskArray);
}
輸出結(jié)果
任務(wù)ID:4, 結(jié)果:0
任務(wù)ID:10, 結(jié)果:4
任務(wù)ID:7, 結(jié)果:1
任務(wù)ID:8, 結(jié)果:2
任務(wù)ID:10, 結(jié)果:7
任務(wù)ID:11, 結(jié)果:5
任務(wù)ID:9, 結(jié)果:3
任務(wù)ID:12, 結(jié)果:6
任務(wù)ID:7, 結(jié)果:8
任務(wù)ID:8, 結(jié)果:9
Task阻塞的三種方式
Wait()
: 等待單個(gè)線(xiàn)程任務(wù)完成WaitAll():
來(lái)指定等待的一個(gè)或多個(gè)線(xiàn)程結(jié)束WaitAny():
來(lái)指定等待任意一個(gè)線(xiàn)程任務(wù)結(jié)束
static void test3()
{
// 方式一: wait方法
Task t = Task.Run(() => { Console.WriteLine("方式1:任務(wù)1......"); }) ;
// 等待 上述任務(wù)完成
t.Wait();
Console.WriteLine("方式一結(jié)束..........");
// 方式二: waitAll 方法
Task tt = Task.Run(() => { Console.WriteLine("方式2:任務(wù)1......"); });
Task tt2 = Task.Run(() => { Console.WriteLine("方式2:任務(wù)2......"); });
Task.WaitAll(tt,tt2);
Console.WriteLine("方式二結(jié)束..........");
// 方式三:waitAny 方法
Task ttt = Task.Run(() => { Console.WriteLine("方式3:任務(wù)1......"); });
Task ttt2 = Task.Run(() => { Console.WriteLine("方式3:任務(wù)2......"); });
Task.WaitAny(ttt, ttt2);
Console.WriteLine("方式三結(jié)束..........");
}
輸出結(jié)果
方式1:任務(wù)1......
方式一結(jié)束..........
方式2:任務(wù)1......
方式2:任務(wù)2......
方式二結(jié)束..........
方式3:任務(wù)2......
方式3:任務(wù)1......
方式三結(jié)束..........
Task任務(wù)的延續(xù)
- WhenAll().ContinueWith() : 作用是當(dāng)
WhenAll()
中指定的線(xiàn)程任務(wù)完成后再執(zhí)行ContinueWith()
中的任務(wù),也就是線(xiàn)程任務(wù)的延續(xù)。而由于這個(gè)等待是異步的,因此不會(huì)給主線(xiàn)程造成阻塞 - WhenAll(task1,task2,...): Task的靜態(tài)方法,作用是異步等待指定任務(wù)完成后,返回結(jié)果。當(dāng)線(xiàn)程任務(wù)有返回值時(shí),返回Task
對(duì)象,否則返回Task對(duì)象。 - WhenAny() :用法與WhenAll()是一樣的,不同的是只要指定的任意一個(gè)線(xiàn)程任務(wù)完成則立即返回結(jié)果。
- ContinueWith(): Task類(lèi)的實(shí)例方法,異步創(chuàng)建當(dāng)另一任務(wù)完成時(shí)可以執(zhí)行的延續(xù)任務(wù)。也就是當(dāng)調(diào)用對(duì)象的線(xiàn)程任務(wù)完成后,執(zhí)行ContinueWith()中的任務(wù)
static void test4()
{
Task t = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("我是線(xiàn)程任務(wù)....."); });
// 異步創(chuàng)建延續(xù)任務(wù)
Task.WhenAll(t).ContinueWith((data) => { Console.WriteLine("我是延續(xù)任務(wù)...."); });
Console.WriteLine("這是主線(xiàn)程........");
Console.ReadKey();
}
輸出結(jié)果
這是主線(xiàn)程........
我是線(xiàn)程任務(wù).....
我是延續(xù)任務(wù)....
注:Task任務(wù)的延續(xù) 與 上面阻塞相比,主要的好處就是 延續(xù)是異步的不會(huì)阻塞主線(xiàn)程
Task的父子任務(wù)
- TaskCreationOptions.AttachedToParent: 用于將子任務(wù)依附到父任務(wù)中
static void test5()
{
// 建立一個(gè)父任務(wù)
Task parentTask = new Task(() => {
// 創(chuàng)建兩個(gè)子任務(wù),依附在父任務(wù)上
Task.Factory.StartNew(() => { Console.WriteLine("子task1任務(wù)。。。。。。"); }, TaskCreationOptions.AttachedToParent);
Task.Factory.StartNew(() => { Console.WriteLine("子task2任務(wù)。。。。。。"); }, TaskCreationOptions.AttachedToParent);
Thread.Sleep(1000);
Console.WriteLine("我是父任務(wù)........");
});
parentTask.Start();
parentTask.Wait();
Console.WriteLine("這里是主線(xiàn)程.......");
Console.ReadKey();
}
輸出結(jié)果
子task2任務(wù)。。。。。。
子task1任務(wù)。。。。。。
我是父任務(wù)........
這里是主線(xiàn)程.......
**Task中的任務(wù)取消 **
Task
中的取消功能使用的是CanclelationTokenSource
,即取消令牌源對(duì)象,可用于解決多線(xiàn)程任務(wù)中協(xié)作取消和超時(shí)取消
- CancellationToken Token: CanclelationTokenSource類(lèi)的屬性成員,返回CancellationToken對(duì)象,可以在開(kāi)啟或創(chuàng)建線(xiàn)程時(shí)作為參數(shù)傳入。
- IsCancellationRequested: 表示當(dāng)前任務(wù)是否已經(jīng)請(qǐng)求取消。Token類(lèi)中也有此屬性成員,兩者互相關(guān)聯(lián)。
- Cancel(): CanclelationTokenSource類(lèi)的實(shí)例方法,取消線(xiàn)程任務(wù),同時(shí)將自身以及關(guān)聯(lián)的Token對(duì)象中的IsCancellationRequested屬性置為true。
- CancelAfter(int millisecondsDelay) :CanclelationTokenSource類(lèi)的實(shí)例方法,用于延遲取消線(xiàn)程任務(wù)。
取消任務(wù)的兩種情況
- 情況一: 通過(guò)Cancel()方法
- 情況二: 通過(guò)CancelAfter(milliseconds) 方法
static void test6()
{
// 情況一: 直接取消
// 創(chuàng)建取消令牌源對(duì)象
CancellationTokenSource cst = new CancellationTokenSource();
//第二個(gè)參數(shù)傳入取消令牌
Task t = Task.Run(() => {
while (!cst.IsCancellationRequested)
{
Thread.Sleep(500);
Console.WriteLine("情況一,沒(méi)有接收到取消信號(hào)......");
}
}, cst.Token);
Thread.Sleep(1000);
//1秒后結(jié)束
cst.Cancel();
Console.ReadKey();
// 情況二: 延遲取消
CancellationTokenSource cst2 = new CancellationTokenSource();
Task t2 = Task.Run(() => {
while (!cst2.IsCancellationRequested)
{
Console.WriteLine("情況二,沒(méi)有接收到取消信號(hào)......");
}
}, cst2.Token);
//1秒后結(jié)束
cst2.CancelAfter(1000);
Console.ReadKey();
}
**Task跨線(xiàn)程訪(fǎng)問(wèn)界面控件 **
通過(guò) TaskScheduler.FromCurrentSynchronizationContext() 獲取TaskScheduler,并將其放入Task的start() 方法中 或 放入延續(xù)方法中即可
- 放入start() 方法中
- 放入 ContinueWith() 延續(xù)方法中
// 通過(guò)start方法
private void button1_Click(object sender, EventArgs e)
{
Task t = new Task(() =>
{
// 為界面控件賦值
this.textBox1.Text = "線(xiàn)程內(nèi)賦值";
});
task.Start(TaskScheduler.FromCurrentSynchronizationContext());
}
// 通過(guò)延續(xù)方法
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
Thread.Sleep(1000);
}).ContinueWith(t => {
this.textBox1.Text = "線(xiàn)程內(nèi)賦值";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Task的異常處理
異常捕獲
- Task線(xiàn)程的異常處理 不能直接將線(xiàn)程對(duì)象相關(guān)代碼try-catch來(lái)捕獲 ,需要通過(guò)調(diào)用線(xiàn)程對(duì)象的wait()函數(shù)來(lái)進(jìn)行線(xiàn)程的異常捕獲
- 線(xiàn)程的異常會(huì)聚合到AggregateException異常對(duì)象中(AggregateException是專(zhuān)門(mén)用來(lái)收集線(xiàn)程異常的異常類(lèi)),多個(gè)異常 需要通過(guò)遍歷該異常對(duì)象來(lái)獲取異常信息
- 如果捕獲到線(xiàn)程異常之后,還想繼續(xù)往上拋出,就需要調(diào)用AggregateException對(duì)象的Handle函數(shù),并返回false。(Handle函數(shù)遍歷了一下AggregateException對(duì)象中的異常)
static void test7()
{
Task t = Task.Run(() =>
{
throw new Exception("異常拋出.....");
});
try
{
t.Wait();
}
catch (AggregateException ex)
{
Console.Error.WriteLine(ex.Message);
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine("內(nèi)異常:"+item.Message);
}
//將異常往外拋出
// ex.Handle(p => false);
}
Console.ReadKey();
}
輸出結(jié)果
One or more errors occurred. (異常拋出.....)
內(nèi)異常:異常拋出.....
-
微軟
+關(guān)注
關(guān)注
4文章
6668瀏覽量
105355 -
線(xiàn)程
+關(guān)注
關(guān)注
0文章
507瀏覽量
20068 -
Thread
+關(guān)注
關(guān)注
2文章
87瀏覽量
26356
發(fā)布評(píng)論請(qǐng)先 登錄
鴻蒙內(nèi)核源碼Task/線(xiàn)程技術(shù)分析

高速接口MIPI DPHY配置task函數(shù)

RAW task 篇
task問(wèn)題
AWR1642開(kāi)發(fā),使用Task_create創(chuàng)建了多個(gè)task,請(qǐng)問(wèn)task都沒(méi)有運(yùn)行是怎么回事?
【HarmonyOS】Task/線(xiàn)程管理篇
簡(jiǎn)談FPGA verilog中的task用法
如何進(jìn)行Android中Task任務(wù)棧的分配

帶你了解 TensorFlow Lite Task Library模型接口
RTA OS系列介紹01-Task
verilog中的task用法
Verdi查看task內(nèi)部變量

評(píng)論