2013-05-29 41 views
20

正在等待線程安全嗎?看來Task類是線程安全的,所以我想等待它也是線程安全的,但我沒有在任何地方找到確認。自定義awaiter也是線程安全的要求 - 我的意思是對於IsCompleted,GetAwaiter等方法?即如果這些方法不是線程安全的,將等待線程安全嗎?不過,我不希望很快就需要自定義服務器。可以等待來自多個線程的相同任務嗎 - 是否等待線程安全?

用戶場景的一個例子:比方說,我有一個後臺任務,異步返回結果,然後從多個線程使用:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 

namespace scratch1 
{ 
    class Foo 
    { 
     Task<int> _task; 

     public Foo() 
     { 
      _task = Task.Run(async() => { await Task.Delay(5000); return 5; }); 
     } 

     // Called from multiple threads 
     public async Task<int> Bar(int i) 
     { 
      return (await _task) + i; 
     } 
    } 

    class Program 
    { 
     public static void Main(string[] args) 
     { 
      Foo foo = new Foo(); 

      List<Task> tasks = new List<Task>(); 
      foreach (var i in Enumerable.Range(0, 100)) 
      { 
       tasks.Add(Task.Run(async() => { Console.WriteLine(await foo.Bar(i)); })); 
      } 

      Task.WaitAll(tasks.ToArray()); 
     } 
    } 
} 

回答

14

正如你提到的,await足夠薄,它不首先看不到線程。

await(狀態機中的編譯器生成的代碼以及BCL中的支持類)關聯的代碼一次只能在一個線程上運行。 (即將從awaitable回來的時候它可以切換到不同的線程,但之前的運行將已經完成)

實際線程安全取決於你正在等待的對象。
它必須有一個線程安全的方式來添加回調(或一次兩次可能會中斷的await)。

此外,它必須調用回調恰好一次,或者它可能會破壞狀態機。 (特別是,如果它同時在兩個線程上運行回調,事情將會變得非常糟糕)

Task是線程安全的。

+5

你的第一個語句可能誤導人們以爲'await'是從根本上不安全的......而我想你的意思是本質上既不安全也不不安全的 - 它只是代表們無論是等待,如果是那樣的'Task',你」很好。 –

+0

@JonSkeet:好點。這是否更好? – SLaks

+0

絕對,謝謝。儘管「只能在一個線程上運行」應該可能「只會一次運行在一個線程上,每個方法調用(對應於狀態機的單個實例)」。它當然可以在'await'點之間的線程之間跳轉,但它不會在多個線程上同時運行。 (至少不要除非你濫用它......這總是很有趣。) –