Посмотрим, как можно немного оптимизировать работу async/await при написании собственной библиотеки с асинхронными методами.
SynchronizationContext и код приложения
При каждом вызове await сохраняется текущий SynchronizationContext. Это необходимо для возврата в исходный контекст после завершения работы асинхронного метода. Вот пример:
var data = await source.GetDataAsync();
this.UpdateInfo(data);
Если здесь вызов GetDataAsync() был сделан из UI-потока, то и UpdateInfo() должен выполнится в нем же. Такое поведение естественно для кода самого приложения и важно для корректной его работы.
А что с библиотеками?
А вот для кода библиотек все как раз наоборот. В большинстве случаев нельзя предсказать где и как будут использованы её методы. Однако, даже если это было сделано в UI-потоке, то его SynchronizationContext уже был сохранен и в дальнейшем работа будет продолжена в его же контексте.
Теперь предположим, что код библиотечного метода сам совершает асинхронные вызовы. Есть ли необходимость сохранять текущий контекст (по сути неизвестного потока)? В большинстве случаев – нет, за редкими исключениями вроде UI компонент.
API .NET 4.5 содержит метод ConfigureAwait(), который позволяет настраивать поведение await. Его единственный булевский параметр указывает на необходимость сохранения контекста (что и происходит по умолчанию). Например, отменим его в следующем вызове:
string content = await httpClient.GetStringAsync(uri).ConfigureAwait(false);
Оптимизация в данном случае заключается в отсутствии расходов на сохранение и восстановлении SynchronizationContext. А это может быть особенно заметно, если такой метод вызывается в цикле.