Понимание контекста в C # 5 async / await

Правильно ли я, что async / await не имеет ничего общего с параллелизмом / параллелизмом и является не чем иным, как реализацией стиля продолжения (CPS)? А реальная потоковая передача выполняется SynchronizationContext экземпляром, который await передает / восстанавливает?

Если это верно, у меня есть следующий вопрос о SynchronizationContext:
он гарантирует, что продолжение будет выполнено в том же потоке.

Однако есть ли какие-либо гарантии того, что контекстная информация потока сохраняется? Я имею в виду Name, CurrentPrincipal, CurrentCulture, CurrentUICulture и т. Д. Зависит ли это от фреймворка (ASP.NET, WinForms, WCF, WPF)?


person UserControl    schedule 17.09.2012    source источник
comment
В общем, SynchronizationContext не гарантирует, что продолжение будет выполнено в том же потоке. Некоторые контексты для этого (WPF, Winforms), но другие нет (ASP.NET).   -  person svick    schedule 17.09.2012
comment
Вы можете установить культуру по умолчанию для потоков, как это.   -  person Mike Fuchs    schedule 06.11.2014


Ответы (2)


Правильно ли я, что async / await не имеет ничего общего с параллелизмом / параллелизмом и является не чем иным, как реализацией CPS?

Что ж, async / await - это переписывание с использованием CPS, так что ваше основное понимание правильное.

Что касается «параллелизма» и «параллелизма», я бы сказал, что он действительно обеспечивает параллелизм; вы можете запускать несколько async операций, которые все находятся «в полете» одновременно. Это легко сделать с помощью Task.WhenAll и Task.WhenAny.

Кроме того, хотя async сам по себе не подразумевает "многопоточность", Task.Run действительно обеспечивает легкую async-совместимую многопоточность.

А реальная потоковая передача выполняется экземпляром SynchronizationContext, который await передает / восстанавливает?

Подумайте об этом так: продолжение, созданное перезаписью CPS, должно запускаться где-то. Захваченный «асинхронный контекст» можно использовать для планирования продолжения.

Боковое примечание: захваченный контекст на самом деле SynchronizationContext.Current , если он не равен нулю, и в этом случае захваченный контекст - это TaskScheduler.Current.

Еще одно важное замечание: захват и восстановление контекста фактически зависит от объекта "awaiter". Итак, по умолчанию, если вы await a Task (или любой другой встроенный объект ожидания), контекст будет захвачен и восстановлен. Но если вы await результат ConfigureAwait(false), то контекст не улавливается. Точно так же, если вы await ваш собственный ожидаемый объект, он не будет захватывать контекст (если вы не запрограммируете его для этого).

Однако есть ли какие-либо гарантии того, что контекстная информация потока сохраняется? Я имею в виду Name, CurrentPrincipal, CurrentCulture, CurrentUICulture и т. Д.

SynchronizationContext отличается от ExecutionContext. Упрощенный ответ состоит в том, что ExecutionContext всегда "течет", поэтому CurrentPrincipal течет (если нет, это может быть проблемой безопасности, поэтому API-интерфейсы, не использующие поток ExecutionContext, всегда заканчиваются на Unsafe).

В приложениях пользовательского интерфейса культура не передается, но по умолчанию она одинакова для всех потоков. Name определенно не будет выполняться, если вы не возобновите работу в том же потоке (например, используя пользовательский интерфейс SynchronizationContext).


Для дальнейшего чтения я рекомендую начать с моего собственного async / await учебника, а затем официальный async / await FAQ. Затем взгляните на сообщение в блоге Стивена Туба о ExecutionContext vs. SynchronizationContext .

Вам также может быть полезна моя SynchronizationContext статья.

person Stephen Cleary    schedule 17.09.2012
comment
Спасибо. Ссылка на сообщение в блоге Стивена Туба о ExecutionContext и SynchronizationContext мне очень помогла. - person Scott; 12.04.2013
comment
Чувак, ты вообще асинхронен. Как и везде. Отлично! - person Jess; 02.07.2015

Нет, ключевые слова _1 _ / _ 2_ имеют прямое отношение к параллелизму. _3 _ / _ 4_ в основном превращают код вашего метода в задачу и продолжение. Чтобы увидеть точный перевод, который производит компилятор (с помощью библиотеки параллельных задач), дизассемблируйте фрагмент кода. Этот перевод употребления _5 _ / _ 6_ «похож» (но не идентичен!) Приведенному ниже примеру.

async Task<int> TaskOfTResult_MethodAsync()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();

это примерно преобразовано в

private int Result()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

где вы ждете возврата вне метода, например

int? hours = null;
Task<int> task = null;
task = Task.Factory.StartNew<int>(() => Result());
task.ContnueWith(cont => 
{
    // Some task completion checking...
    hours = task.Result;
}, CancellationToken.None, 
   TaskCreationOptions.None, 
   TaskScheduler.Current);

Или вы можете поместить код TPL в метод Result

private int ResultAsync()
{
    int? hours = null;
    Task<int> task = null;
    task = Task.Factory.StartNew<int>(() => 
    {
        int hours;
        // . . .
        // Return statement specifies an integer result.
        return hours;
    }, CancellationToken.None, 
       TaskCreationOptions.None, 
       TaskScheduler.Current);
    try
    {
        return task.Result;
    }
    catch (AggregateException aggEx)
    {
        // Some handler method for the agg exception.
        aggEx.Handle(HandleException); 
    }
}

SynchronizationContext не гарантирует, что продолжение будет выполнено в том же потоке для кода _13 _ / _ 14_. Однако вы можете установить контекст с помощью кода TPL с помощью ключевого слова SynchronisationContex.

person MoonKnight    schedule 17.09.2012