c#之task與thread區別及其使用


1.什么是thread
當我們提及多線程的時候會想到thread和threadpool,這都是異步操作,threadpool其實就是thread的集合,具有很多優勢,不過在任務多的時候全局隊列會存在競爭而消耗資源。thread默認為前台線程,主程序必須等線程跑完才會關閉,而threadpool相反。
總結:threadpool確實比thread性能優,但是兩者都沒有很好的api區控制,如果線程執行無響應就只能等待結束,從而誕生了task任務。
2.什么是task
 task簡單地看就是任務,那和thread有什么區別呢?Task的背后的實現也是使用了線程池線程,但它的性能優於ThreadPoll,因為它使用的不是線程池的全局隊列,而是使用的本地隊列,使線程之間的資源競爭減少。同時Task提供了豐富的API來管理線程、控制。但是相對前面的兩種耗內存,Task依賴於CPU對於多核的CPU性能遠超前兩者,單核的CPU三者的性能沒什么差別。

3.創建一個task任務有兩種模式:
1.使用factory創建會直接執行,使用new創建不會執行,必須等到start啟動之后才執行。


public void test()
{
var testTask = new Task(() =>
{
Console.WriteLine("task start");
});
testTask.Start();
var factoryTeak = Task.Factory.StartNew(() =>
{
Console.WriteLine("factory task start");
});
}


2.我們來看看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);


輸出結果:

Created
task start
Running
Running
RanToCompletion
RanToCompletion

可以看出task確實是異步執行,並且wait很好地控制了task。

3.下面演示幾個控制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");
通過wait()對單個task進行等待,Task.waitall()對多個task進行等待,waitany()執行任意一個task就往下繼續執行。

4.task的回調執行

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的取消

首先創建一個取消task的令牌的實例,在不啟動task直接取消:

var tokenSource = new CancellationTokenSource();//創建取消task實例
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);
 輸出結果:

Created
task is to cancel
Canceled

如果task啟動了真的取消了task?

var tokenSource = new CancellationTokenSource();//創建取消task實例
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);
}
輸出結果:

Created
WaitingToRun
task is to cancel
Running
Running
Running
Running
Running
Running
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion

 可以看出其實並沒有取消task,此時task還在繼續跑。

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");
此時為普通關聯,父task和子task沒影響

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");
此時為父task和子task關聯,wait會一直等待父子task執行完。

6.task死鎖的問題

我們可以設置最大等待時間,如果超過了等待時間,就不再等待,下面我們來修改代碼,設置最大等待時間為5秒(項目中可以根據實際情況設置),如果超過5秒就輸出哪個任務出錯了

7.對Spinlock的使用

舉例來說Parallel.for和Parallel.foreach是線程不安全的,有可能達不到你的預期,此時就需要加鎖來解決此問題,我們可以加lock和spinlock(自旋鎖)來解決

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);
輸出結果:

Num1的值為:1660913202
Num2的值為:4999950000
Num3的值為:4999950000

Num1的值為:2754493646
Num2的值為:4999950000
Num3的值為:4999950000

Num1的值為:4999950000
Num2的值為:4999950000
Num3的值為:4999950000

最后看看threadpoll和task的結構圖:

threadpool:

 

 task:


原文鏈接:https://blog.csdn.net/qq_40677590/article/details/102797838


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM