2014-11-15 2 views
5

Я читал о SynchronizationContext и его использовании с методами async/wait (link). По моему мнению, в приложении Console, где SynchronizationContext имеет значение null, продолжение ожидаемого метода (Task) будет запланировано с планировщиком по умолчанию, который будет ThreadPool.Продолжение задачи не было запланировано в потоке потока пула

Но если я запускаю эту консоль приложение, вы будете видеть на выходе, что продолжение выполняется на рабочем потоке, который я создал:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("MainThreadId=" + Thread.CurrentThread.ManagedThreadId); 
     Method1().ContinueWith(t => 
     { 
      Console.WriteLine("After Method1. ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
     }); 

     Console.ReadKey(); 
    } 

    public static async Task Method1() 
    { 
     Console.WriteLine("Method1 => Entered. ThreadId=" + Thread.CurrentThread.ManagedThreadId); 

     TaskCompletionSource<bool> completionSource = new TaskCompletionSource<bool>(); 
     Thread thread = new Thread(() => 
     { 
      Console.WriteLine("Method1 => Started new thread. ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
      Thread.Sleep(2000); 
      completionSource.SetResult(true); 
     }); 

     thread.Start(); 

     await completionSource.Task; 

     Console.WriteLine("Method1 => After WorkerThread. ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
    } 
} 

И вот результат:

MainThreadId=10 
Method1 => Entered. ThreadId=10 
Method1 => Started new thread. ThreadId=11 
Method1 => After WorkerThread. ThreadId=11 
After Method1. ThreadId=12 

Как вы можете видеть, "После WorkerThread" был выведен на тот же поток, что и мой рабочий, но не на threadpool.

Я нашел аналогичный question, но парень использовал Моно, и они говорили, что это ошибка. На моей стороне я построил этот код в Visual Studio и запустил его под Windows 7 и .Net 4.5.2, установленный на моей машине.

Не могли бы вы объяснить это поведение?

ответ

5

Это из-за implementation detail that I documented on my blog: продолжение, созданное await, планируется использовать с использованием флага ExecuteSynchronously. В этом случае, когда пришло время запуска продолжения (т. Е. В вызове TaskCompletionSource<T>.SetResult рабочего потока), планировщик по умолчанию сначала пытается определить, может ли он работать в текущем потоке.

Since the worker thread has no TaskScheduler that will reject executing the task synchronously, флаг ExecuteSynchronously будет cause the thread pool task scheduler to just execute the task synchronously (т. Е. На вызывающей нити).

+0

Хорошая добыча! Итак, это [статья] (http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx?PageIndex=2#comments) неверно, говоря, что «Когда ожидаемая задача завершается , продолжение будет содержать оставшуюся часть асинхронного метода.Если захваченный SynchronizationContext имеет значение NULL, то RestOfMethod() будет выполняться в исходном TaskScheduler (который часто является TaskScheduler.Default, то есть ThreadPool) "? – Absolom

+2

@Absolom: Технически он выполняется в планировщике задач пула потоков. Нечетное поведение объясняется тем, что планировщик задач пула потоков решает, что ваш поток является частью его контекста. –

+0

Спасибо за разъяснение, но не могли бы вы объяснить, почему в моем примере «After Method1» запланирован планировщиком задач пула потоков и фактически запущен в пуле потоков, а не в том же потоке. Почему это не похоже на поведение Async и Task? – Absolom

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