Продолжение задачи не запланировано в потоке пула потоков

Я читал о SynchronizationContext и его использовании с методами async/await ( ссылка). Насколько я понимаю, в консольном приложении, где 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

Как видите, "After WorkerThread" был выведен в том же потоке, что и мой рабочий поток, но не в пуле потоков.

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

Может ли кто-нибудь объяснить это поведение?

1 ответ

Решение

Это из-за деталей реализации, которые я задокументировал в своем блоге: продолжение, созданное await планируется с использованием ExecuteSynchronously флаг. В том случае, когда пришло время запустить продолжение (т. Е. В TaskCompletionSource<T>.SetResult вызовите рабочий поток), планировщик по умолчанию сначала пытается определить, может ли он работать в текущем потоке.

Так как рабочий поток не имеет TaskScheduler который отклонит выполнение задачи синхронно, ExecuteSynchronously флаг заставит планировщик задач пула потоков просто выполнять задачу синхронно (т. е. в вызывающем потоке).

Другие вопросы по тегам