Я думал, что ожидание продолжается в той же теме, что и вызывающий абонент, но, похоже, не

Я подумал, что один из моментов, касающихся 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;
}
Другие вопросы по тегам