2013-05-29 3 views
20

Ожидает, что поток безопасен? Кажется, класс Task является потокобезопасным, поэтому я ожидаю, что он также потокобезопасен, но я нигде не нашел подтверждения. Также существует проблема безопасности потоков для пользовательского awaiter - я имею в виду методы IsCompleted, GetAwaiter и т. Д.? То есть если эти методы не являются потокобезопасными, ожидают потокобезопасности? Тем не менее, я не ожидаю, что в ближайшее время вам понадобится пользовательский awaiter.Можно ли ждать одной задачи из нескольких потоков - ожидание потокобезопасности?

Пример сценария пользователя: Допустим, у меня есть фоновая задача, которая возвращает результат асинхронно, который затем используется из нескольких потоков:

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) будет выполняться только по одному потоку за раз. (он может переключиться на другую нить при возврате с ожидаемого, но предыдущий запуск уже завершен)

Фактическая безопасность потока зависит от объекта, который вы ожидаете.
Он должен иметь потокобезопасный способ добавления обратных вызовов (или await одновременное его разворачивание).

Кроме того, он должен вызвать обратный вызов ровно один раз, или он может сломать конечный автомат. (в частности, если он выполняет обратный вызов на двух потоках одновременно, все будет чересчур неправильным)

Task является потокобезопасным.

+5

Ваше первое заявление может ввести людей в заблуждение, считая, что 'await' является принципиально небезопасным ... тогда как я думаю, вы подразумеваете, что он не является неотъемлемо безопасным и небезопасным - он просто делегирует все, что ожидает, и если это' Задача', хорошо. –

+0

@JonSkeet: Хорошая точка. Это лучше? – SLaks

+0

Определенно, спасибо. Хотя «будет работать только один поток», вероятно, должно быть «будет выполняться только по одному потоку за раз, за ​​вызов метода (который соответствует одному экземпляру конечного автомата)». Он может, конечно, прыгать между потоками в точках 'await', но он не будет запускаться одновременно на нескольких потоках. (По крайней мере, если вы не злоупотребляете им ужасно ... это всегда весело.) –

Смежные вопросы