Поиск…


Псевдокод для ключевых слов async / await

Рассмотрим простой асинхронный метод:

async Task Foo()
{
    Bar();
    await Baz();
    Qux();
}

Упрощение, мы можем сказать, что этот код на самом деле означает следующее:

Task Foo()
{
    Bar();
    Task t = Baz();
    var context = SynchronizationContext.Current;
    t.ContinueWith(task) =>
    {
        if (context == null)
            Qux();
        else
            context.Post((obj) => Qux(), null);
    }, TaskScheduler.Current);

    return t;
}

Это означает, что async слова async / await используют текущий контекст синхронизации, если он существует. Т.е. вы можете написать код библиотеки, который будет корректно работать в пользовательских интерфейсах, веб-и консольных приложениях.

Исходная статья .

Отключение синхронизации

Чтобы отключить контекст синхронизации, вы должны вызвать метод ConfigureAwait :

async Task() Foo()
{
    await Task.Run(() => Console.WriteLine("Test"));
}

. . .

Foo().ConfigureAwait(false);

ConfigureAwait предоставляет средства для предотвращения поведения по умолчанию SynchronizationContext; передача false для параметра flowContext запрещает использование SynchronizationContext для возобновления выполнения после ожидания.

Цитата из It's All About SynchronizationContext .

Почему SynchronizationContext так важен?

Рассмотрим этот пример:

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = RunTooLong();
}

Этот метод заморозит приложение пользовательского интерфейса до тех пор, пока RunTooLong не будет завершен. Приложение будет неактуальным.

Вы можете попробовать выполнить внутренний код асинхронно:

private void button1_Click(object sender, EventArgs e)
{
    Task.Run(() => label1.Text = RunTooLong());
}

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

private void button1_Click(object sender, EventArgs e)
{
    Task.Run(() =>
    {
        var label1Text = RunTooLong();

        if (label1.InvokeRequired)
            lable1.BeginInvoke((Action) delegate() { label1.Text = label1Text; });
        else
            label1.Text = label1Text;
    });
}

Теперь не забудьте всегда использовать этот шаблон. Или попробуйте SynchronizationContext.Post , который сделает это для вас:

private void button1_Click(object sender, EventArgs e)
{
    Task.Run(() =>
    {
        var label1Text = RunTooLong();
        SynchronizationContext.Current.Post((obj) =>
        {
            label1.Text = label1    Text);
        }, null);
    });
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow