Я думал, что ожидание продолжается в той же теме, что и вызывающий абонент, но, похоже, не
Я подумал, что один из моментов, касающихся async / await, заключается в том, что когда задача завершается, продолжение запускается в том же контексте, когда вызывается 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, чтобы изменить поведение контекста продолжения!
3 ответа
Когда ты await
по умолчанию await
Оператор захватит текущий "контекст" и использовать его для возобновления async
метод.
Этот "контекст" SynchronizationContext.Current
если это не null
в каком случае это TaskScheduler.Current
, (Если нет выполняемой в данный момент задачи, то TaskScheduler.Current
такой же как TaskScheduler.Default
, планировщик задач пула потоков).
Важно отметить, что SynchronizationContext
или же TaskScheduler
не обязательно подразумевает конкретную тему. Пользовательский интерфейс SynchronizationContext
запланирует работу в потоке пользовательского интерфейса; но ASP.NET SynchronizationContext
не будет планировать работу для определенного потока.
Я подозреваю, что причиной вашей проблемы является то, что вы вызываете async
код слишком рано Когда приложение запускается, оно просто имеет обычный старый обычный поток. Этот поток становится потоком пользовательского интерфейса только тогда, когда Application.Run
,
await
выражение будет использовать значение SynchronizationContext.Current
вернуть поток управления обратно в поток, в котором это произошло. В тех случаях, когда это null
по умолчанию будет TaskScheduler.Current
, Реализация полагается исключительно на это значение для изменения контекста потока, когда Task
значение завершено. Похоже, в этом случае await
захватывает контекст, который не связан с потоком пользовательского интерфейса
По умолчанию Windows Forms устанавливает контекст синхронизации при создании первого элемента управления.
Итак, вы можете сделать так:
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var f = new Form1();
//var s=SynchronizationContext.Current;
//var s2=TaskScheduler.Current;
GetAsync();
Application.Run(f);
}
static async Task<bool> GetAsync()
{
MessageBox.Show($"thread id {Thread.CurrentThread.ManagedThreadId}");
bool flag = await Task.Factory.StartNew<bool>(() =>
{
Thread.Sleep(1000);
return true;
});
MessageBox.Show($"thread id {Thread.CurrentThread.ManagedThreadId}");
return flag;
}