什么是Task?
描述
- Task出現之前,微軟的多線程處理方式有:Thread→ThreadPool→委托的異步調用,雖然可以滿足基本業(yè)務場景,但它們在多個線程的等待處理方面、資源占用方面、延續(xù)和阻塞方面都顯得比較笨拙,在面對復雜的業(yè)務場景下,顯得有點捉襟見肘
- Task是微軟在.Net 4.0時代推出來的,也是微軟極力推薦的一種多線程的處理方式,Task看起來像一個Thread,實際上,它是在ThreadPool的基礎上進行的封裝
- Task的控制和擴展性很強,在線程的延續(xù)、阻塞、取消、超時等方面遠勝于Thread和ThreadPool
- Task可以簡單看作相當于Thead+TheadPool,其性能比直接使用Thread要更好,在工作中更多的是使用Task來處理多線程任務
任務Task和線程Thread的區(qū)別
- Task是建立在Thread之上的,最終其實還是由Thread去執(zhí)行,它們都是在 System.Threading 命名空間下的
- Task跟Thread并不是一對一的關系。比如說開啟10個任務并不一定會開啟10個線程,因為使用Task開啟新任務時,是從線程池中調用線程,這點與ThreadPool.QueueUserWorkItem類似
Task的使用
創(chuàng)建Task的三種方式
- 方式一:通過創(chuàng)建Task對象后調用其 Start()函數
- 方式二:調用Task的靜態(tài)方法Run()
- 方式三:通過Task工廠,新建一個線程
// 方式一,通過Start
Task t1 = new Task(() => { Console.WriteLine("我是Task方式一"); });
t1.Start();
// 方式二,通過Run
Task t2= Task.Run(()=>{Console.WriteLine("我是Task方式二"); });
// 方式三,通過工廠
Task t3= Task.Factory.StartNew(()=>{Console.WriteLine("我是Task方式三"); });
帶返回值與不帶返回值的Task
static void Main()
{
// 沒有返回參數
Task t1 = new Task(() => { Console.WriteLine("我是Task沒有返回參數"); });
t1.Start();
// 有返回參數
Task<int> t2 = new Task<int>(() => { return 1+1; });
t2.Start();
int result = t2.Result;
Console.WriteLine(result);
}
輸出結果
我是Task沒有返回參數
2
一次性建立多個任務場景
static void test1()
{
Task[] taskArray = new Task[10];
for (int i = 0; i < 10; i++)
{
int bb = i;
Task t = Task.Run(() => { Console.WriteLine("任務ID:{0}, 結果:{1}",Thread.CurrentThread.ManagedThreadId, bb); });
taskArray[i] = t;
}
// 等待所有任務完成
Task.WaitAll(taskArray);
}
輸出結果
任務ID:4, 結果:0
任務ID:10, 結果:4
任務ID:7, 結果:1
任務ID:8, 結果:2
任務ID:10, 結果:7
任務ID:11, 結果:5
任務ID:9, 結果:3
任務ID:12, 結果:6
任務ID:7, 結果:8
任務ID:8, 結果:9
Task阻塞的三種方式
Wait()
: 等待單個線程任務完成WaitAll():
來指定等待的一個或多個線程結束WaitAny():
來指定等待任意一個線程任務結束
static void test3()
{
// 方式一: wait方法
Task t = Task.Run(() => { Console.WriteLine("方式1:任務1......"); }) ;
// 等待 上述任務完成
t.Wait();
Console.WriteLine("方式一結束..........");
// 方式二: waitAll 方法
Task tt = Task.Run(() => { Console.WriteLine("方式2:任務1......"); });
Task tt2 = Task.Run(() => { Console.WriteLine("方式2:任務2......"); });
Task.WaitAll(tt,tt2);
Console.WriteLine("方式二結束..........");
// 方式三:waitAny 方法
Task ttt = Task.Run(() => { Console.WriteLine("方式3:任務1......"); });
Task ttt2 = Task.Run(() => { Console.WriteLine("方式3:任務2......"); });
Task.WaitAny(ttt, ttt2);
Console.WriteLine("方式三結束..........");
}
輸出結果
方式1:任務1......
方式一結束..........
方式2:任務1......
方式2:任務2......
方式二結束..........
方式3:任務2......
方式3:任務1......
方式三結束..........
Task任務的延續(xù)
- WhenAll().ContinueWith() : 作用是當
WhenAll()
中指定的線程任務完成后再執(zhí)行ContinueWith()
中的任務,也就是線程任務的延續(xù)。而由于這個等待是異步的,因此不會給主線程造成阻塞 - WhenAll(task1,task2,...): Task的靜態(tài)方法,作用是異步等待指定任務完成后,返回結果。當線程任務有返回值時,返回Task
對象,否則返回Task對象。 - WhenAny() :用法與WhenAll()是一樣的,不同的是只要指定的任意一個線程任務完成則立即返回結果。
- ContinueWith(): Task類的實例方法,異步創(chuàng)建當另一任務完成時可以執(zhí)行的延續(xù)任務。也就是當調用對象的線程任務完成后,執(zhí)行ContinueWith()中的任務
static void test4()
{
Task t = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("我是線程任務....."); });
// 異步創(chuàng)建延續(xù)任務
Task.WhenAll(t).ContinueWith((data) => { Console.WriteLine("我是延續(xù)任務...."); });
Console.WriteLine("這是主線程........");
Console.ReadKey();
}
輸出結果
這是主線程........
我是線程任務.....
我是延續(xù)任務....
注:Task任務的延續(xù) 與 上面阻塞相比,主要的好處就是 延續(xù)是異步的不會阻塞主線程
Task的父子任務
- TaskCreationOptions.AttachedToParent: 用于將子任務依附到父任務中
static void test5()
{
// 建立一個父任務
Task parentTask = new Task(() => {
// 創(chuàng)建兩個子任務,依附在父任務上
Task.Factory.StartNew(() => { Console.WriteLine("子task1任務。。。。。。"); }, TaskCreationOptions.AttachedToParent);
Task.Factory.StartNew(() => { Console.WriteLine("子task2任務。。。。。。"); }, TaskCreationOptions.AttachedToParent);
Thread.Sleep(1000);
Console.WriteLine("我是父任務........");
});
parentTask.Start();
parentTask.Wait();
Console.WriteLine("這里是主線程.......");
Console.ReadKey();
}
輸出結果
子task2任務。。。。。。
子task1任務。。。。。。
我是父任務........
這里是主線程.......
**Task中的任務取消 **
Task
中的取消功能使用的是CanclelationTokenSource
,即取消令牌源對象,可用于解決多線程任務中協作取消和超時取消
- CancellationToken Token: CanclelationTokenSource類的屬性成員,返回CancellationToken對象,可以在開啟或創(chuàng)建線程時作為參數傳入。
- IsCancellationRequested: 表示當前任務是否已經請求取消。Token類中也有此屬性成員,兩者互相關聯。
- Cancel(): CanclelationTokenSource類的實例方法,取消線程任務,同時將自身以及關聯的Token對象中的IsCancellationRequested屬性置為true。
- CancelAfter(int millisecondsDelay) :CanclelationTokenSource類的實例方法,用于延遲取消線程任務。
取消任務的兩種情況
- 情況一: 通過Cancel()方法
- 情況二: 通過CancelAfter(milliseconds) 方法
static void test6()
{
// 情況一: 直接取消
// 創(chuàng)建取消令牌源對象
CancellationTokenSource cst = new CancellationTokenSource();
//第二個參數傳入取消令牌
Task t = Task.Run(() => {
while (!cst.IsCancellationRequested)
{
Thread.Sleep(500);
Console.WriteLine("情況一,沒有接收到取消信號......");
}
}, cst.Token);
Thread.Sleep(1000);
//1秒后結束
cst.Cancel();
Console.ReadKey();
// 情況二: 延遲取消
CancellationTokenSource cst2 = new CancellationTokenSource();
Task t2 = Task.Run(() => {
while (!cst2.IsCancellationRequested)
{
Console.WriteLine("情況二,沒有接收到取消信號......");
}
}, cst2.Token);
//1秒后結束
cst2.CancelAfter(1000);
Console.ReadKey();
}
**Task跨線程訪問界面控件 **
通過 TaskScheduler.FromCurrentSynchronizationContext() 獲取TaskScheduler,并將其放入Task的start() 方法中 或 放入延續(xù)方法中即可
- 放入start() 方法中
- 放入 ContinueWith() 延續(xù)方法中
// 通過start方法
private void button1_Click(object sender, EventArgs e)
{
Task t = new Task(() =>
{
// 為界面控件賦值
this.textBox1.Text = "線程內賦值";
});
task.Start(TaskScheduler.FromCurrentSynchronizationContext());
}
// 通過延續(xù)方法
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
Thread.Sleep(1000);
}).ContinueWith(t => {
this.textBox1.Text = "線程內賦值";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Task的異常處理
異常捕獲
- Task線程的異常處理 不能直接將線程對象相關代碼try-catch來捕獲 ,需要通過調用線程對象的wait()函數來進行線程的異常捕獲
- 線程的異常會聚合到AggregateException異常對象中(AggregateException是專門用來收集線程異常的異常類),多個異常 需要通過遍歷該異常對象來獲取異常信息
- 如果捕獲到線程異常之后,還想繼續(xù)往上拋出,就需要調用AggregateException對象的Handle函數,并返回false。(Handle函數遍歷了一下AggregateException對象中的異常)
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("內異常:"+item.Message);
}
//將異常往外拋出
// ex.Handle(p => false);
}
Console.ReadKey();
}
輸出結果
One or more errors occurred. (異常拋出.....)
內異常:異常拋出.....
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。
舉報投訴
-
微軟
+關注
關注
4文章
6651瀏覽量
105244 -
線程
+關注
關注
0文章
507瀏覽量
20021 -
Thread
+關注
關注
2文章
85瀏覽量
26289
發(fā)布評論請先 登錄
相關推薦
鴻蒙內核源碼Task/線程技術分析
前言 在鴻蒙內核中,廣義上可理解為一個Task就是一個線程 一、怎么理解Task 1. 官方文檔是怎么描述線程 基本概念 從系統的角度看,線程是競爭系統資源的最小運行單元。線程可以使用或等待CPU

RAW task 篇
這篇文檔會主要選擇些API講解,讀者可以對著代碼仔細理解。1 RAW_U16 raw_task_create(RAW_TASK_OBJ*task_obj, RAW_U8*task
發(fā)表于 02-27 14:00
task問題
背景如下:小弟想用一個task實現16個時鐘周期的延遲,然后產生一標志位cnt_key用作后面語句的激勵,但用modlesim仿真的時候cnt_key沒有起到激勵作用,程序如下,請各路大神幫忙指點下
發(fā)表于 06-12 10:11
AWR1642開發(fā),使用Task_create創(chuàng)建了多個task,請問task都沒有運行是怎么回事?
AWR1642開發(fā),使用Task_create創(chuàng)建了多個task,task都沒有運行是怎么回事。在main中創(chuàng)建了task,還需要在哪里初始化嗎?
發(fā)表于 08-08 08:29
【HarmonyOS】Task/線程管理篇
原文鏈接:https://my.oschina.net/u/3751245/blog/4595539本文分析Task/線程管理源碼 詳見:los_task.c目錄前言一、怎么理解Task1. 官方
發(fā)表于 10-19 14:54
簡談FPGA verilog中的task用法
????????大家好,又到了每日學習的時間了,今天我們來聊一聊FPGA verilog中的task用法。 ? ? ? ?任務就是一段封裝在“task-endtask”之間的程序。任務是通過調用
如何進行Android中Task任務棧的分配
這意思就是說Task實際上是一個Activity棧,通常用戶感受的一個Application就是一個Task。從這個定義來看,Task跟Service或者其他Components是沒有任何聯系的,它
發(fā)表于 07-03 17:42
?0次下載

帶你了解 TensorFlow Lite Task Library模型接口
額外的代碼來處理復雜的邏輯,如數據轉換、預處理/后處理、加載關聯文件等。 額外的代碼 今天,我們將為大家介紹 TensorFlow Lite Task Library,這是一組功能強大且易于使用的模型
RTA OS系列介紹01-Task
AUTOSAR OS主要包含Task, ISRs, Events, Resources, Application, Counter, Alarms, Schedule Table等OS對象。后續(xù)將對如上提到的八個對象進行分別介紹,本篇介紹的內容為Task,下面進入正題:
verilog中的task用法
任務就是一段封裝在“task-endtask”之間的程序。任務是通過調用來執(zhí)行的,而且只有在調用時才執(zhí)行,如果定義了任務,但是在整個過程中都沒有調用它,那么這個任務是不會執(zhí)行的。調用某個任務時可能
verilog中的task用法介紹
任務就是一段封裝在“task-endtask”之間的程序。任務是通過調用來執(zhí)行的,而且只有在調用時才執(zhí)行
verilog中function和task的區(qū)別
在Verilog中,Function和Task是用于模塊化設計和重用代碼的兩種重要元素。它們允許開發(fā)人員將復雜的操作分解為更小的功能單元,并在需要時調用它們。雖然Function和Task在某些方面
verilog task和function區(qū)別
verilog中的task和function都是用于實現模塊中的可重復的功能,并且可以接收參數和返回結果。但是它們在編寫和使用上有一些區(qū)別。下面將詳細介紹task和function的區(qū)別。 語法結構
評論