Я думал, что ждут продолжения в той же теме, что и вызывающий, но, похоже, это не так
Я думал, что один из вопросов об async/await заключается в том, что когда задача завершается, продолжение выполняется в том же контексте, когда был вызван ожидание, что в моем случае было бы потоком пользовательского интерфейса.
Итак, например:
Debug.WriteLine("2: Thread ID: " + Thread.CurrentThread.ManagedThreadId);
await fs.ReadAsync(data, 0, (int)fs.Length);
Debug.WriteLine("3: Thread ID: " + Thread.CurrentThread.ManagedThreadId);
Я бы этого не ожидал:
2: Thread ID: 10
3: Thread ID: 11
Что дает? Почему идентификатор потока для продолжения отличается от потока пользовательского интерфейса?
В соответствии с этой статьей [^] мне нужно будет явно вызвать ConfigureAwait, чтобы изменить поведение контекста продолжения!
Ответы
Ответ 1
Когда вы await
, по умолчанию оператор await
будет захватывать текущий "контекст" и использовать его для возобновления метода async
.
Этот "контекст" равен SynchronizationContext.Current
, если он не равен null
, и в этом случае он TaskScheduler.Current
. (Если в настоящее время нет текущей задачи, то TaskScheduler.Current
совпадает с TaskScheduler.Default
, планировщиком задач пула потоков).
Важно отметить, что a SynchronizationContext
или TaskScheduler
не обязательно подразумевает конкретный поток. Пользовательский интерфейс SynchronizationContext
будет планировать работу с потоком пользовательского интерфейса; но ASP.NET SynchronizationContext
не будет планировать работу для определенного потока.
Я подозреваю, что причиной вашей проблемы является то, что вы вызываете код async
слишком рано. Когда приложение запускается, у него просто обычный обычный поток. Этот поток становится только потоком пользовательского интерфейса, когда он делает что-то вроде Application.Run
.
Ответ 2
В выражении await
будет использоваться значение SynchronizationContext.Current
, чтобы вернуть поток управления обратно в поток, на котором он произошел. В случаях, когда это значение null
, оно по умолчанию будет TaskScheduler.Current
. Реализация опирается исключительно на это значение, чтобы изменить контекст потока, когда значение Task
завершено. Похоже, что в этом случае await
захватывает контекст, который не связан с потоком пользовательского интерфейса