Слово «захват» слишком непрозрачно, оно звучит слишком похоже на то, что должно быть реализовано во фреймворке. Вводит в заблуждение, поскольку обычно это происходит в программе, которая использует одну из реализаций SynchronizationContext по умолчанию. Как и один вы попадаете в приложение Winforms. Но когда вы пишете свой собственный, фреймворк больше не помогает, и это становится вашей работой.
Связь async / await дает контексту возможность запустить продолжение (код после ожидания) в определенном потоке. Звучит банально, раз уж вы так часто делали раньше, но на самом деле это довольно сложно. Невозможно произвольно прервать код, выполняемый этим потоком, что могло бы вызвать ужасные ошибки повторного входа. Поток должен помочь, он должен решить стандартную проблему производителя-потребителя. Принимает потокобезопасную очередь и цикл, который очищает эту очередь, обрабатывая запросы вызова. Задача переопределенных методов Post и Send - добавлять запросы в очередь, задача потока - использовать цикл для его очистки и выполнения запросов.
Основной поток приложения Winforms, WPF или UWP имеет такой цикл, он выполняется Application.Run (). С соответствующим контекстом синхронизации, который знает, как подавать в него запросы на вызов, соответственно WindowsFormsSynchronizationContext, DispatcherSynchronizationContext и WinRTSynchronizationContext. ASP.NET тоже может это делать, используя AspNetSynchronizationContext. Все это предоставляется фреймворком и автоматически устанавливается сантехникой из библиотеки классов. Они фиксируют контекст синхронизации в своем конструкторе и используют Begin / Invoke в своих методах Post и Send.
Когда вы пишете свой собственный SynchronizationContext, вы должны позаботиться об этих деталях. В вашем фрагменте вы не переопределяли Post и Send, но унаследовали базовые методы. Они ничего не знают и могут выполнить запрос только в произвольном потоке пула потоков. Итак, SynchronizationContext.Current теперь имеет значение null в этом потоке, поток пула потоков не знает, откуда пришел запрос.
Создать свой собственный не так уж и сложно, ConcurrentQueue и делегаты помогают значительно сократить код. Так поступили многие программисты, часто цитируют эту библиотеку. Но за это придется заплатить серьезную цену: этот цикл диспетчера в корне меняет поведение приложения в консольном режиме. Он блокирует нить до тех пор, пока петля не закончится. Как и Application.Run ().
Вам нужен совсем другой стиль программирования, знакомый вам по приложениям с графическим интерфейсом. Код не может длиться слишком долго, поскольку он закрывает цикл диспетчера, предотвращая отправку запросов на вызов. В приложении с графическим интерфейсом это заметно по тому, что пользовательский интерфейс перестает отвечать, в вашем примере кода вы заметите, что ваш метод медленно завершается, поскольку продолжение не может работать некоторое время. Вам нужен рабочий поток, чтобы выделять медленный код, бесплатного обеда нет.
Стоит отметить, почему этот материал существует. У приложений с графическим интерфейсом есть серьезная проблема, их библиотеки классов никогда не являются поточно-ориентированными, и их нельзя сделать безопасными с помощью lock
. Единственный способ правильно их использовать - выполнять все вызовы из одного потока. InvalidOperationException, если вы этого не сделаете. Их цикл диспетчера поможет вам в этом, включив Begin / Invoke и async / await. Консоль не имеет этой проблемы, любой поток может что-то записать в консоль, и блокировка может помочь предотвратить смешивание их вывода. Таким образом, консольному приложению не требуется настраиваемый контекст синхронизации. YMMV.
person
Hans Passant
schedule
07.10.2018
Post
реализация неверна. Он должен гарантировать, что делегат вызывается в правильном контексте.base.Post
не делает этого за вас. - person user4003407   schedule 07.10.2018MySC
не является базовым типом SC; означает, чтоsyncCtx.GetType() != typeof(SynchronizationContext)
верно. - person KeyBored   schedule 07.10.2018Post
неверен .. это просто фальшивая реализация в учебных целях :) - person KeyBored   schedule 07.10.2018Post
называется. На этом захват контекста заканчивается. Он отвечает только за вызовPost
в захваченном контексте и ничего больше. Все остальноеPost
ответственность исполнителя. - person user4003407   schedule 07.10.2018