Понимание контекста в С# 5 async/wait

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

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

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

Ответ 1

Я уверен, что async/await сам по себе не имеет ничего общего с concurrency/parallelism и не что иное, как реализация CPS?

Ну, async/await - это переписывание, использующее CPS, поэтому ваше основное понимание верное.

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

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

И реальная потоковая обработка выполняется экземпляром SynchronizationContext, который ожидает прохождения/восстановления?

Подумайте об этом так: продолжение, созданное переписыванием 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 Часто задаваемые вопросы. Затем посмотрите сообщение в блоге Stephen Toub на ExecutionContext и SynchronizationContext.

Вы также можете найти мою SynchronizationContext статью.

Ответ 2

Нет, ключевые слова async/await имеют все, что связано с concurrency. async/await в основном переносит ваш код метода в задачу и продолжение. Чтобы увидеть точный перевод, который производит компилятор (используя параллельную библиотеку задач), разобрать фрагмент кода. Этот перевод использования async/await "похож" (но не идентичен!) На пример ниже

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 не гарантирует, что продолжение будет выполнено в том же потоке для кода async/awate. Однако вы можете установить контекст с помощью кода TPL с помощью ключевого слова SynchronisationContex.