Найти в Дзене
using Dev

Поговорим о ConfigureAwait ч.1

В .NET добавили async/ await к языкам и библиотекам более семи лет назад. За это время он распространился как лесной пожар не только по всей экосистеме .NET, но и был воспроизведен во множестве других языков и платформ. Также было замечено множество улучшений в .NET с точки зрения дополнительных языковых конструкций, использующих асинхронность, API, предлагающих поддержку асинхронности, и фундаментальных улучшений в инфраструктуре, которая делает async/ awaitтикает (в частности, улучшения производительности и возможности диагностики в .NET Core). . Однако один из аспектов async/ await, который продолжает вызывать вопросы, — это ConfigureAwait. В этом посте я надеюсь ответить на многие из них. Я хочу, чтобы этот пост был удобен для чтения от начала до конца, а также представлял собой список часто задаваемых вопросов (FAQ), который можно использовать в качестве справочной информации в будущем. Чтобы действительно понять ConfigureAwait, нам нужно начать немного раньше… Что такое контекст

В .NET добавили async/ await к языкам и библиотекам более семи лет назад. За это время он распространился как лесной пожар не только по всей экосистеме .NET, но и был воспроизведен во множестве других языков и платформ. Также было замечено множество улучшений в .NET с точки зрения дополнительных языковых конструкций, использующих асинхронность, API, предлагающих поддержку асинхронности, и фундаментальных улучшений в инфраструктуре, которая делает async/ awaitтикает (в частности, улучшения производительности и возможности диагностики в .NET Core). .

Однако один из аспектов async/ await, который продолжает вызывать вопросы, — это ConfigureAwait. В этом посте я надеюсь ответить на многие из них. Я хочу, чтобы этот пост был удобен для чтения от начала до конца, а также представлял собой список часто задаваемых вопросов (FAQ), который можно использовать в качестве справочной информации в будущем.

Чтобы действительно понять ConfigureAwait, нам нужно начать немного раньше…

Что такое контекст синхронизации?

В System.Threading.SynchronizationContextдокументации говорится, что он «обеспечивает базовую функциональность для распространения контекста синхронизации в различных моделях синхронизации». Не совсем очевидное описание.

Для варианта использования 99,9% SynchronizationContext— это просто тип, предоставляющий виртуальный Post метод, который требует асинхронного выполнения делегата (есть множество других виртуальных членов SynchronizationContext, но они используются гораздо реже и не имеют значения для этого обсуждения). . Post Буквально базовый тип просто вызывает ThreadPool.QueueUserWorkItem асинхронный вызов предоставленного делегата. Однако производные типы имеют переопределение Post, позволяющее выполнить этот делегат в наиболее подходящем месте и в наиболее подходящее время.

Например, Windows Forms имеет SynchronizationContextпроизводный тип , который переопределяет Post эквивалент Control.BeginInvoke; это означает, что любые вызовы его Post метода приведут к вызову делегата в какой-то более поздний момент в потоке, связанном с этим соответствующим элементом управления, также известном как «поток пользовательского интерфейса». Windows Forms опирается на обработку сообщений Win32 и имеет «цикл сообщений», работающий в потоке пользовательского интерфейса, который просто ожидает поступления новых сообщений для обработки. Эти сообщения могут быть для движений и щелчков мыши, для набора текста с клавиатуры, для системных событий, для доступных для вызова делегатов и т. д. Итак, учитывая SynchronizationContext экземпляр потока пользовательского интерфейса приложения Windows Forms, чтобы заставить делегата выполнить его, UI-поток, его просто нужно передать в Post.

То же самое касается Windows Presentation Foundation (WPF). Он имеет собственный SynchronizationContextпроизводный тип с Post переопределением, которое аналогичным образом «маршалирует» делегата в поток пользовательского интерфейса (через Dispatcher.BeginInvoke), в данном случае управляемом диспетчером WPF, а не элементом управления Windows Forms.

И для среды выполнения Windows (WinRT). Он имеет собственный SynchronizationContextпроизводный тип с Post переопределением, которое также ставит делегата в очередь в поток пользовательского интерфейса через его CoreDispatcher.

Это выходит за рамки простого «запуска этого делегата в потоке пользовательского интерфейса». Любой может реализовать SynchronizationContextс, Post который делает что угодно. Например, меня может не волновать, в каком потоке работает делегат, но я хочу быть уверен, что все делегаты Post, переданные мне, SynchronizationContext выполняются с некоторой ограниченной степенью параллелизма. Я могу добиться этого с помощью SynchronizationContextтакого :

-2

Фактически, платформа модульного тестирования xunit предоставляетSynchronizationContext очень похожую систему, которую она использует для ограничения объема кода, связанного с тестами, которые могут выполняться одновременно.

Преимущество всего этого такое же, как и любой абстракции: оно предоставляет единый API, который можно использовать для постановки делегата в очередь для обработки по желанию создателя реализации, без необходимости знать детали этой реализации. Итак, если я пишу библиотеку и хочу пойти поработать, а затем поставить делегата в очередь обратно в «контекст» исходного местоположения, мне просто нужно захватить их SynchronizationContext, удержать их, а затем, когда Я закончил свою работу, вызываю Post этого контекста, чтобы передать делегат, которого я хочу вызвать. Мне не нужно знать, что для Windows Forms я должен захватить a Control и использовать его BeginInvoke, или для WPF я должен захватить a Dispatcher и использовать его BeginInvoke, или для xunit я должен каким-то образом получить его контекст и поставить к нему очередь; Мне просто нужно захватить ток SynchronizationContext и использовать его позже. Для этого SynchronizationContext предоставляется Current свойство, позволяющее для достижения вышеупомянутой цели написать такой код:

-3

Платформа, которая хочет предоставить пользовательский контекст, Current использует этот SynchronizationContext.SetSynchronizationContext метод.

Источник

to be continued...