Task.Run 和 Task.Factory.StartNew 區別


Task.Run 是在 dotnet framework 4.5 之后才可以使用, Task.Factory.StartNew 可以使用比 Task.Run 更多的參數,可以做到更多的定制。

可以認為 Task.Run 是簡化的 Task.Factory.StartNew 的使用,除了需要指定一個線程是長時間占用的,否則就使用 Task.Run

創建新線程

下面來告訴大家使用兩個函數創建新的線程

Task.Run(() =>
{
   var foo = 2;
});

這時 foo 的創建就在另一個線程,需要知道 Task.Run 用的是線程池,也就是不是調用這個函數就會一定創建一個新的線程,但是會在另一個線程運行。

Task.Factory.StartNew(() =>
{
    ar foo = 2;
});

可以看到,兩個方法實際上是沒有差別,但是Task.Run比較好看,所以推薦使用Task.Run

等待線程

創建的線程,如果需要等待線程執行完成在繼續,那么可以使用 await 等待

復制代碼

private static async void SeenereKousa()
{
     Console.WriteLine("開始 線程"+Thread.CurrentThread.ManagedThreadId);
     await Task.Run(() =>
     {
         Console.WriteLine("進入 線程" + Thread.CurrentThread.ManagedThreadId);
     });
     Console.WriteLine("退出 線程"+Thread.CurrentThread.ManagedThreadId);
}

復制代碼

但是需要說的是這里使用 await 主要是給函數調用的外面使用,上面代碼在函數里面使用 await 函數是 void 那么和把代碼放在 task 里面是相同

復制代碼

private static async void SeenereKousa()
{
    Console.WriteLine("開始 線程"+Thread.CurrentThread.ManagedThreadId);
    await Task.Run(() =>
    {
         Console.WriteLine("進入 線程" + Thread.CurrentThread.ManagedThreadId);
         Console.WriteLine("退出 線程"+Thread.CurrentThread.ManagedThreadId);
    });
}

復制代碼

但是如果把 void 修改為 Task ,那么等待線程才有用

除了使用 await 等待,還可以使用 WaitAll 等待

復制代碼

 Console.WriteLine("開始 線程" + Thread.CurrentThread.ManagedThreadId);
 var t = Task.Run(() =>
 {
      Console.WriteLine("進入 線程" + Thread.CurrentThread.ManagedThreadId);
 });

 Task.WaitAll(t);
 Console.WriteLine("退出 線程" + Thread.CurrentThread.ManagedThreadId);

復制代碼

使用 WaitAll 是在調用 WaitAll 的線程等待,也就是先在線程 1 運行,然后異步到 線程2 運行,這時線程1 等待線程2運行完成再繼續,所以輸出

  1.  
    開始 線程1
  2.  
    進入 線程2
  3.  
    退出 線程1

長時間運行

兩個函數最大的不同在於 Task.Factory.StartNew 可以設置線程是長時間運行,這時線程池就不會等待這個線程回收

復制代碼

Task.Factory.StartNew(() =>
{
      for (int i = 0; i < 100; i++)
      {
           var foo = 2;
      }
      Console.WriteLine("進行 線程" + Thread.CurrentThread.ManagedThreadId);
}, TaskCreationOptions.LongRunning);

復制代碼

所以在需要設置線程是長時間運行的才需要使用 Task.Factory.StartNew 不然就使用 Task.Run

調用 Task.Run(foo) 就和使用下面代碼一樣

Task.Factory.StartNew(foo, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

最后再說明下await和async與Task的聯系:
async標記會告訴編輯器接下來的方法可能會用到異步,當然並不一定會有用到,一直向下到await這是會等待,await 不會開啟新的線程,
當前線程會一直往下走直到遇到真正的Async方法(比如說HttpClient.GetStringAsync),這個方法的內部會用Task.Run或者Task.Factory.StartNew 去開啟線程(因為task底層是線程池機制,也可能是復用線程)。
如果方法不是.NET為我們提供的Async方法,我們需要自己創建Task,才會真正的去創建線程。


免責聲明!

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



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