c#中task與thread區(qū)別及其使用的方法示例
1.什么是thread
當(dāng)我們提及多線程的時(shí)候會(huì)想到thread和threadpool,這都是異步操作,threadpool其實(shí)就是thread的集合,具有很多優(yōu)勢(shì),不過(guò)在任務(wù)多的時(shí)候全局隊(duì)列會(huì)存在競(jìng)爭(zhēng)而消耗資源。thread默認(rèn)為前臺(tái)線程,主程序必須等線程跑完才會(huì)關(guān)閉,而threadpool相反。
總結(jié):threadpool確實(shí)比thread性能優(yōu),但是兩者都沒(méi)有很好的api區(qū)控制,如果線程執(zhí)行無(wú)響應(yīng)就只能等待結(jié)束,從而誕生了task任務(wù)。
2.什么是task
task簡(jiǎn)單地看就是任務(wù),那和thread有什么區(qū)別呢?Task的背后的實(shí)現(xiàn)也是使用了線程池線程,但它的性能優(yōu)于ThreadPoll,因?yàn)樗褂玫牟皇蔷€程池的全局隊(duì)列,而是使用的本地隊(duì)列,使線程之間的資源競(jìng)爭(zhēng)減少。同時(shí)Task提供了豐富的API來(lái)管理線程、控制。但是相對(duì)前面的兩種耗內(nèi)存,Task依賴于CPU對(duì)于多核的CPU性能遠(yuǎn)超前兩者,單核的CPU三者的性能沒(méi)什么差別。
3.創(chuàng)建一個(gè)task任務(wù)有兩種模式:
1.使用factory創(chuàng)建會(huì)直接執(zhí)行,使用new創(chuàng)建不會(huì)執(zhí)行,必須等到start啟動(dòng)之后才執(zhí)行。
? public void test()
{
var testTask = new Task(() =>
{
Console.WriteLine("task start");
});
testTask.Start();
var factoryTeak = Task.Factory.StartNew(() =>
{
Console.WriteLine("factory task start");
});
}
?2.我們來(lái)看看task的生命周期
?var testTask = new Task(() =>
{
Console.WriteLine("task start");
System.Threading.Thread.Sleep(2000);
});
Console.WriteLine(testTask.Status);
testTask.Start();
Console.WriteLine(testTask.Status);
Console.WriteLine(testTask.Status);
testTask.Wait();
Console.WriteLine(testTask.Status);
Console.WriteLine(testTask.Status);
?輸出結(jié)果:
Created
task start
Running
Running
RanToCompletion
RanToCompletion
可以看出task確實(shí)是異步執(zhí)行,并且wait很好地控制了task。
3.下面演示幾個(gè)控制task的方法
var testTask = new Task(() =>
{
Console.WriteLine("task start");
System.Threading.Thread.Sleep(2000);
});
testTask.Start();
testTask.Wait();
var testTask = new Task(() =>
{
Console.WriteLine("task start");
System.Threading.Thread.Sleep(2000);
});
testTask.Start();
var factoryTeak = Task.Factory.StartNew(() =>
{
Console.WriteLine("factory task start");
});
Task.WaitAll(testTask, factoryTeak);
Console.WriteLine("end");
? var testTask = new Task(() =>
{
Console.WriteLine("task start");
System.Threading.Thread.Sleep(2000);
});
testTask.Start();
var factoryTeak = Task.Factory.StartNew(() =>
{
Console.WriteLine("factory task start");
});
Task.WaitAny(testTask, factoryTeak);
Console.WriteLine("end");通過(guò)wait()對(duì)單個(gè)task進(jìn)行等待,Task.waitall()對(duì)多個(gè)task進(jìn)行等待,waitany()執(zhí)行任意一個(gè)task就往下繼續(xù)執(zhí)行。
4.task的回調(diào)執(zhí)行
var testTask = new Task(() =>
{
Console.WriteLine("task start");
System.Threading.Thread.Sleep(2000);
});
testTask.Start();
var resultTest = testTask.ContinueWith<string>((Task) => {
Console.WriteLine("testTask end");
return "end";
});
Console.WriteLine(resultTest.Result);5.task的取消
首先創(chuàng)建一個(gè)取消task的令牌的實(shí)例,在不啟動(dòng)task直接取消:
var tokenSource = new CancellationTokenSource();//創(chuàng)建取消task實(shí)例
var testTask = new Task(() =>
{
for (int i = 0; i < 6; i++) {
System.Threading.Thread.Sleep(1000);
}
},tokenSource.Token);
Console.WriteLine(testTask.Status);
tokenSource.Token.Register(()=> {
Console.WriteLine("task is to cancel");
});
tokenSource.Cancel();
Console.WriteLine(testTask.Status);輸出結(jié)果:
Created
task is to cancel
Canceled
如果task啟動(dòng)了真的取消了task?
var tokenSource = new CancellationTokenSource();//創(chuàng)建取消task實(shí)例
var testTask = new Task(() =>
{
for (int i = 0; i <6; i++) {
System.Threading.Thread.Sleep(1000);
}
},tokenSource.Token);
Console.WriteLine(testTask.Status);
testTask.Start();
Console.WriteLine(testTask.Status);
tokenSource.Token.Register(()=> {
Console.WriteLine("task is to cancel");
});
tokenSource.Cancel();
Console.WriteLine(testTask.Status);
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine(testTask.Status);
}輸出結(jié)果:
Created
WaitingToRun
task is to cancel
Running
Running
Running
Running
Running
Running
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
可以看出其實(shí)并沒(méi)有取消task,此時(shí)task還在繼續(xù)跑。
6.task的嵌套
var parentTask = new Task(()=> {
var childTask = new Task(() =>{
System.Threading.Thread.Sleep(2000);
Console.WriteLine("childTask to start");
});
childTask.Start();
Console.WriteLine("parentTask to start");
});
parentTask.Start();
parentTask.Wait();
Console.WriteLine("end");此時(shí)為普通關(guān)聯(lián),父task和子task沒(méi)影響
var parentTask = new Task(()=> {
var childTask = new Task(() =>{
System.Threading.Thread.Sleep(2000);
Console.WriteLine("childTask to start");
}, TaskCreationOptions.AttachedToParent);
childTask.Start();
Console.WriteLine("parentTask to start");
} );
parentTask.Start();
parentTask.Wait();
Console.WriteLine("end");此時(shí)為父task和子task關(guān)聯(lián),wait會(huì)一直等待父子task執(zhí)行完。
6.task死鎖的問(wèn)題
我們可以設(shè)置最大等待時(shí)間,如果超過(guò)了等待時(shí)間,就不再等待,下面我們來(lái)修改代碼,設(shè)置最大等待時(shí)間為5秒(項(xiàng)目中可以根據(jù)實(shí)際情況設(shè)置),如果超過(guò)5秒就輸出哪個(gè)任務(wù)出錯(cuò)了
7.對(duì)Spinlock的使用
舉例來(lái)說(shuō)Parallel.for和Parallel.foreach是線程不安全的,有可能達(dá)不到你的預(yù)期,此時(shí)就需要加鎖來(lái)解決此問(wèn)題,我們可以加lock和spinlock(自旋鎖)來(lái)解決
SpinLock slock = new SpinLock(false);
var testLock= new object();
long sum1 = 0;
long sum2 = 0;
long sum3 = 0;
Parallel.For(0, 100000, i =>
{
sum1 += i;
});
Parallel.For(0, 100000, i =>
{
bool lockTaken = false;
try
{
slock.Enter(ref lockTaken);
sum2 += i;
}
finally
{
if (lockTaken)
slock.Exit(false);
}
});
Parallel.For(0, 100000, i =>
{
lock(testLock)
{
sum3 += i;
};
});
Console.WriteLine("Num1的值為:{0}", sum1);
Console.WriteLine("Num2的值為:{0}", sum2);
Console.WriteLine("Num3的值為:{0}", sum3);輸出結(jié)果:
Num1的值為:1660913202
Num2的值為:4999950000
Num3的值為:4999950000
Num1的值為:2754493646
Num2的值為:4999950000
Num3的值為:4999950000
Num1的值為:4999950000
Num2的值為:4999950000
Num3的值為:4999950000
最后看看threadpoll和task的結(jié)構(gòu)圖:
threadpool:

task:

相關(guān)文章
C#基于TCP實(shí)現(xiàn)簡(jiǎn)單游戲客戶端的完整實(shí)例
這篇文章主要給大家介紹了關(guān)于C#基于TCP實(shí)現(xiàn)簡(jiǎn)單游戲客戶端的相關(guān)資料,通過(guò)本文介紹的方法可以直接實(shí)現(xiàn)游戲客戶端,是個(gè)非常適合學(xué)習(xí)的實(shí)例需要的朋友可以參考下2021-11-11
C#使用HttpWebRequest與HttpWebResponse模擬用戶登錄
這篇文章主要為大家詳細(xì)介紹了C#使用HttpWebRequest與HttpWebResponse模擬用戶登錄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
使用VS2010 C#開(kāi)發(fā)ActiveX控件(下),完整代碼打包下載
我們介紹了開(kāi)發(fā)、打包、發(fā)布、使用ActiveX控件的全過(guò)程。在演示程序中,我們沒(méi)有調(diào)用串口通信和讀卡器Dll程序,由于我們讀卡器的原始Dll是使用其它語(yǔ)言進(jìn)行開(kāi)發(fā)的,對(duì)C#來(lái)說(shuō),是非托管代碼,因此我們還需要在代碼級(jí)別進(jìn)行非托管代碼的安全性設(shè)置2011-05-05
C#實(shí)現(xiàn)多個(gè)計(jì)時(shí)器記錄不同定時(shí)時(shí)間
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)多個(gè)計(jì)時(shí)器記錄不同定時(shí)時(shí)間,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
c# for循環(huán)中創(chuàng)建線程執(zhí)行問(wèn)題
這篇文章主要介紹了有關(guān)c# for循環(huán)中創(chuàng)建線程執(zhí)行問(wèn)題,下面文章將將以舉例的方式展開(kāi)for循環(huán)中創(chuàng)建線程執(zhí)行問(wèn)題的內(nèi)容,需要的朋友可以參考一下,希望對(duì)你有所幫助2021-11-11

