C# 并行和多線程編程——認(rèn)識(shí)和使用Task
對(duì)于多線程,我們經(jīng)常使用的是Thread。在我們了解Task之前,如果我們要使用多核的功能可能就會(huì)自己來(lái)開(kāi)線程,然而這種線程模型在.net 4.0之后被一種稱為基于“任務(wù)的編程模型”所沖擊,因?yàn)閠ask會(huì)比thread具有更小的性能開(kāi)銷,不過(guò)大家肯定會(huì)有疑惑,任務(wù)和線程到底有什么區(qū)別呢?
任務(wù)和線程的區(qū)別:
1、任務(wù)是架構(gòu)在線程之上的,也就是說(shuō)任務(wù)最終還是要拋給線程去執(zhí)行。
2、任務(wù)跟線程不是一對(duì)一的關(guān)系,比如開(kāi)10個(gè)任務(wù)并不是說(shuō)會(huì)開(kāi)10個(gè)線程,這一點(diǎn)任務(wù)有點(diǎn)類似線程池,但是任務(wù)相比線程池有很小的開(kāi)銷和精確的控制。
一、認(rèn)識(shí)Task和Task的基本使用
1、認(rèn)識(shí)Task
首先來(lái)看一下Task的繼承結(jié)構(gòu)。Task標(biāo)識(shí)一個(gè)異步操作。

可以看到Task和Thread一樣,位于System.Threading命名空間下,這也就是說(shuō)他們直接有密不可分的聯(lián)系。下面我們來(lái)仔細(xì)看一下吧!
2、創(chuàng)建Task
創(chuàng)建Task的方法有兩種,一種是直接創(chuàng)建——new一個(gè)出來(lái),一種是通過(guò)工廠創(chuàng)建。下面來(lái)看一下這兩種創(chuàng)建方法:
//第一種創(chuàng)建方式,直接實(shí)例化
var task1 = new Task(() =>
{
//TODO you code
});
這是最簡(jiǎn)單的創(chuàng)建方法,可以看到其構(gòu)造函數(shù)是一個(gè)Action,其構(gòu)造函數(shù)有如下幾種,比較常用的是前兩種。

//第二種創(chuàng)建方式,工廠創(chuàng)建
var task2 = Task.Factory.StartNew(() =>
{
//TODO you code
});
這種方式通過(guò)靜態(tài)工廠,創(chuàng)建以個(gè)Task并運(yùn)行。下面我們來(lái)建一個(gè)控制臺(tái)項(xiàng)目,演示一下,代碼如下:
要添加System.Threading.Tasks命名控件引用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TaskDemo
{
class Program
{
static void Main(string[] args)
{
var task1 = new Task(() =>
{
Console.WriteLine("Hello,task");
});
task1.Start();
var task2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Hello,task started by task factory");
});
Console.Read();
}
}
}
這里我分別用兩種方式創(chuàng)建兩個(gè)task,并讓他們運(yùn)行。可以看到通過(guò)構(gòu)造函數(shù)創(chuàng)建的task,必須手動(dòng)Start,而通過(guò)工廠創(chuàng)建的Task直接就啟動(dòng)了。
下面我們來(lái)看一下Task的聲明周期,編寫如下代碼:
var task1 = new Task(() =>
{
Console.WriteLine("Begin");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Finish");
});
Console.WriteLine("Before start:" + task1.Status);
task1.Start();
Console.WriteLine("After start:" + task1.Status);
task1.Wait();
Console.WriteLine("After Finish:" + task1.Status);
Console.Read();
task1.Status就是輸出task的當(dāng)前狀態(tài),其輸出結(jié)果如下:

可以看到調(diào)用Start前的狀態(tài)是Created,然后等待分配線程去執(zhí)行,到最后執(zhí)行完成。
從我們可以得出Task的簡(jiǎn)略生命周期:
Created:表示默認(rèn)初始化任務(wù),但是“工廠創(chuàng)建的”實(shí)例直接跳過(guò)。
WaitingToRun: 這種狀態(tài)表示等待任務(wù)調(diào)度器分配線程給任務(wù)執(zhí)行。
RanToCompletion:任務(wù)執(zhí)行完畢。
二、Task的任務(wù)控制
Task最吸引人的地方就是他的任務(wù)控制了,你可以很好的控制task的執(zhí)行順序,讓多個(gè)task有序的工作。下面來(lái)詳細(xì)說(shuō)一下:
1、Task.Wait
在上個(gè)例子中,我們已經(jīng)使用過(guò)了,task1.Wait();就是等待任務(wù)執(zhí)行完成,我們可以看到最后task1的狀態(tài)變?yōu)镃ompleted。
2、Task.WaitAll
看字面意思就知道,就是等待所有的任務(wù)都執(zhí)行完成,下面我們來(lái)寫一段代碼演示一下:
static void Main(string[] args)
{
var task1 = new Task(() =>
{
Console.WriteLine("Task 1 Begin");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Task 1 Finish");
});
var task2 = new Task(() =>
{
Console.WriteLine("Task 2 Begin");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Task 2 Finish");
});
task1.Start();
task2.Start();
Task.WaitAll(task1, task2);
Console.WriteLine("All task finished!");
Console.Read();
}
其輸出結(jié)果如下:

可以看到,任務(wù)一和任務(wù)二都完成以后,才輸出All task finished!
3、Task.WaitAny
這個(gè)用發(fā)同Task.WaitAll,就是等待任何一個(gè)任務(wù)完成就繼續(xù)向下執(zhí)行,將上面的代碼WaitAll替換為WaitAny,輸出結(jié)果如下:

4、Task.ContinueWith
就是在第一個(gè)Task完成后自動(dòng)啟動(dòng)下一個(gè)Task,實(shí)現(xiàn)Task的延續(xù),下面我們來(lái)看下他的用法,編寫如下代碼:
static void Main(string[] args)
{
var task1 = new Task(() =>
{
Console.WriteLine("Task 1 Begin");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Task 1 Finish");
});
var task2 = new Task(() =>
{
Console.WriteLine("Task 2 Begin");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Task 2 Finish");
});
task1.Start();
task2.Start();
var result = task1.ContinueWith<string>(task =>
{
Console.WriteLine("task1 finished!");
return "This is task result!";
});
Console.WriteLine(result.Result.ToString());
Console.Read();
}
執(zhí)行結(jié)果如下:

可以看到,task1完成之后,開(kāi)始執(zhí)行后面的內(nèi)容,并且這里我們?nèi)〉胻ask的返回值。
在每次調(diào)用ContinueWith方法時(shí),每次會(huì)把上次Task的引用傳入進(jìn)來(lái),以便檢測(cè)上次Task的狀態(tài),比如我們可以使用上次Task的Result屬性來(lái)獲取返回值。我們還可以這么寫:
var SendFeedBackTask = Task.Factory.StartNew(() => { Console.WriteLine("Get some Data!"); })
.ContinueWith<bool>(s => { return true; })
.ContinueWith<string>(r =>
{
if (r.Result)
{
return "Finished";
}
else
{
return "Error";
}
});
Console.WriteLine(SendFeedBackTask.Result);
首先輸出Get some data,然后執(zhí)行第二個(gè)獲得返回值true,最后根據(jù)判斷返回Finished或error。輸出結(jié)果:
Get some Data!
Finished
其實(shí)上面的寫法簡(jiǎn)化一下,可以這樣寫:
Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);});
輸出One,這個(gè)可以看明白了吧~
更多ContinueWith用法參見(jiàn):http://technet.microsoft.com/zh-CN/library/dd321405
5、Task的取消
前面說(shuō)了那么多Task的用法,下面來(lái)說(shuō)下Task的取消,比如我們啟動(dòng)了一個(gè)task,出現(xiàn)異?;蛘哂脩酎c(diǎn)擊取消等等,我們可以取消這個(gè)任務(wù)。
如何取消一個(gè)Task呢,我們通過(guò)cancellation的tokens來(lái)取消一個(gè)Task。在很多Task的Body里面包含循環(huán),我們可以在輪詢的時(shí)候判斷IsCancellationRequested屬性是否為True,如果是True的話就return或者拋出異常,拋出異常后面再說(shuō),因?yàn)檫€沒(méi)有說(shuō)異常處理的東西。
下面在代碼中看下如何實(shí)現(xiàn)任務(wù)的取消,代碼如下:
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var task = Task.Factory.StartNew(() =>
{
for (var i = 0; i < 1000; i++)
{
System.Threading.Thread.Sleep(1000);
if (token.IsCancellationRequested)
{
Console.WriteLine("Abort mission success!");
return;
}
}
}, token);
token.Register(() =>
{
Console.WriteLine("Canceled");
});
Console.WriteLine("Press enter to cancel task...");
Console.ReadKey();
tokenSource.Cancel();
Console.ReadKey();//這句忘了加,程序退出了,看不到“Abort mission success!“這個(gè)提示
這里開(kāi)啟了一個(gè)Task,并給token注冊(cè)了一個(gè)方法,輸出一條信息,然后執(zhí)行ReadKey開(kāi)始等待用戶輸入,用戶點(diǎn)擊回車后,執(zhí)行tokenSource.Cancel方法,取消任務(wù)。其輸出結(jié)果如下:

好了,今天先說(shuō)道這里,明天繼續(xù)講task,接下來(lái)該說(shuō)說(shuō)task的異常處理和其他的一些用法,如果喜歡可以關(guān)注我,一有更新會(huì)馬上通知你。
作者:雲(yún)霏霏
以上就是C# 并行和多線程編程——認(rèn)識(shí)和使用Task的詳細(xì)內(nèi)容,更多關(guān)于C# 并行和多線程編程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C#使用讀寫鎖解決多線程并發(fā)問(wèn)題
- C# 使用CancellationTokenSource取消多線程
- C#使用讀寫鎖三行代碼簡(jiǎn)單解決多線程并發(fā)的問(wèn)題
- 解析C#多線程編程中異步多線程的實(shí)現(xiàn)及線程池的使用
- C#使用Parallel類進(jìn)行多線程編程實(shí)例
- C#中WPF使用多線程調(diào)用窗體組件的方法
- C#多線程學(xué)習(xí)之(五)使用定時(shí)器進(jìn)行多線程的自動(dòng)管理
- C#多線程學(xué)習(xí)之(四)使用線程池進(jìn)行多線程的自動(dòng)管理
- C#多線程基本使用小結(jié)
相關(guān)文章
C#多線程處理多個(gè)隊(duì)列數(shù)據(jù)的方法
本文將結(jié)合實(shí)例代碼,介紹C#多線程處理多個(gè)隊(duì)列數(shù)據(jù)的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06
C#開(kāi)發(fā)WinForm根據(jù)條件改變DataGridView行顏色
這篇文章介紹了C#開(kāi)發(fā)WinForm根據(jù)條件改變DataGridView行顏色的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
C#將PDF文檔轉(zhuǎn)換為Markdown文檔的代碼實(shí)現(xiàn)
將PDF文件轉(zhuǎn)換為Markdown格式是一個(gè)非常實(shí)用的需求,尤其是在需要將內(nèi)容從固定布局的PDF文件中提取出來(lái),并轉(zhuǎn)換為更易于編輯和處理的文本格式時(shí),本文將介紹如何通過(guò)C#代碼將PDF文檔轉(zhuǎn)換Markdown(MD)文檔,需要的朋友可以參考下2024-10-10
C#打包應(yīng)用程序,與.NETFramework介紹
C#打包應(yīng)用程序,與.NETFramework介紹,需要的朋友可以參考一下2013-05-05
C#實(shí)現(xiàn)判斷文件夾存在與否并創(chuàng)建文件夾的方法
這篇文章主要介紹了C#實(shí)現(xiàn)判斷文件夾存在與否并創(chuàng)建文件夾的方法,涉及C#針對(duì)文件及目錄的判斷與創(chuàng)建操作相關(guān)技巧,需要的朋友可以參考下2017-02-02
一個(gè)可攜帶附加消息的增強(qiáng)消息框MessageBoxEx
一個(gè)可攜帶附加消息的增強(qiáng)消息框MessageBoxEx分享給大家,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
使用HttpHanlder處理404:File not found的問(wèn)題
本篇文章小編為大家介紹。使用HttpHanlder處理404:File not found的問(wèn)題。需要的朋友參考下2013-04-04

