Когда следует использовать ConfigureAwait (true)

Кто-нибудь сталкивается с сценарием для использования ConfigureAwait(true)? Поскольку true - это параметр по умолчанию, я не вижу, когда вы его когда-либо используете.

Ответ 1

Одна из возможностей, которую я вижу, - это писать код в библиотеке, и вы хотите разрешить своим абонентам решать, следует ли продолжать в исходном контексте 1 (хотя я обычно утверждать, что никогда не продолжайте в исходном контексте из кода библиотеки)

Ваш вызывающий абонент либо передаст параметр bool, либо установит какое-то значение конфигурации, и вы не узнаете до тех пор, пока не увидите, какое значение имеет правильный аргумент.


Это общий тип ответа для API, например, который имеет вариант без аргументов и вариант с одним аргументом, где вариант no-args документируется как "тот же, что и вариант с одним аргументом, известное значение x" - если вы не узнаете до времени выполнения, какое правильное значение будет передано, тогда вы должны просто вызвать вариант с одним аргументом с правильным значением времени выполнения.


1 например. ваш вызывающий абонент также предоставляет делегата. Ваш собеседник будет знать (и может решить), должен ли этот делегат вернуться в исходный контекст или нет.

Ответ 2

true, чтобы попытаться перевести продолжение в исходный контекст; в противном случае - false.

На самом деле это больше похоже на то, что ConfigureAwait(true) похож на .ContinueWith( t => {...}, TaskScheduler.FromCurrentSynchronizationContext()), где ConfigureAwait(false) похож на .ContinueWith( t => {...}). Если вы передадите значение false, то продолжению разрешается запускать поток потоков, вместо того, чтобы возвращаться к текущему контексту синхронизации.

Ответ 3

Так как значение по умолчанию является опцией, я не могу видеть, когда вы когда-либо будете ее использовать.

Один очевидный случай использования - это когда вы хотите убедиться, что каждый раз, когда чего-то ожидают, делается явный и осознанный выбор того, что делать с контекстом синхронизации.

Пример политики из http://newmedialabs.co.za/blog/post/SynchronizationContexts:

В NML мы предпочитаем всегда четко указывать, как мы хотим, чтобы продолжение задачи происходило. Таким образом, хотя заданием по умолчанию является ConfigureAwait (true), мы по-прежнему указываем его как таковое, чтобы всегда быть в курсе того, что происходит "под капотом".

Хотя для улучшения читаемости они используют расширение вместо ConfigureAwait(true) напрямую:

Однако, когда вы смотрите на большое количество кода, некоторые с ConfigureAwait (true) и некоторые с ConfigureAwait (false), нелегко определить, где они отличаются. Поэтому мы используем либо ConfigureAwait (false), либо полезный метод расширения ContinueOnCapturedContext(). Он делает то же самое, но просто отличает его от ConfigureAwait (false) более наглядным способом.

Ответ 4

Ситуация для использования ConfigureAwait(true) возникает при выполнении ожидания в блокировке или при использовании любых других ресурсов, специфичных для контекста/потока. Для этого требуется контекст синхронизации, который вам придется создавать, если вы не используете Windows Forms или WPF, которые автоматически создают контекст синхронизации пользовательского интерфейса.

В следующем коде (предполагается, что он вызывается из потока пользовательского интерфейса и контекста синхронизации), если используется ConfigureAwait(false), блокировки будут пытаться освободиться в другом потоке, вызывая исключение. Это простой пример, который обновляет большой файл конфигурации, если он изменился из внешнего источника, пытаясь избежать записи на диск ввода-вывода, если файл конфигурации такой же, как и раньше.

Пример:

/// <summary>
/// Write a new config file
/// </summary>
/// <param name="xml">Xml of the new config file</param>
/// <returns>Task</returns>
public async Task UpdateConfig(string xml)
{
    // Ensure valid xml before writing the file
    XmlDocument doc = new XmlDocument();
    using (XmlReader xmlReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { CheckCharacters = false }))
    {
        doc.Load(xmlReader);
    }
    // ReaderWriterLock
    configLock.AcquireReaderLock(Timeout.Infinite);
    try
    {
        string text = await File.ReadAllTextAsync(ConfigFilePath).ConfigureAwait(true);

        // if the file changed, update it
        if (text != xml)
        {
            LockCookie cookie = configLock.UpgradeToWriterLock(Timeout.Infinite);
            try
            {
                // compare again in case text was updated before write lock was acquired
                if (text != xml)
                {
                    await File.WriteAllTextAsync(ConfigFilePath, xml).ConfigureAwait(true);
                }
            }
            finally
            {
                configLock.DowngradeFromWriterLock(ref cookie);
            }
        }
    }
    finally
    {
        configLock.ReleaseReaderLock();
    }
}

Ответ 5

Если вы используете долговременные функции Azure, вы должны использовать ConfigureAwait(true), когда ожидаете выполнения своих функций "Активность":

string capture = await context.CallActivityAsync<string>("GetCapture", captureId).ConfigureAwait(true);

В противном случае вы, скорее всего, получите ошибку:

"Обнаружено многопоточное выполнение. Это может произойти, если код функции оркестратора ожидает выполнения задачи, которая не была создана методом DurableOrchestrationContext. Дополнительные сведения можно найти в этой статье https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-checkpointing-and-replay#orchestrator-code-constraints.".

Дополнительную информацию можно найти здесь.