假设您正在编写一个自定义单线程 GUI 库(或任何具有事件循环的库)。根据我的理解,如果我使用async/await
,或者只是常规的 TPL 延续,它们都将安排在TaskScheduler.Current
(or on SynchronizationContext.Current
).
问题是延续可能想要访问库的单线程部分,这意味着它必须在同一个事件循环中执行。例如,给定一个简单的游戏循环,事件可能会按如下方式处理:
// All continuation calls should be put onto this queue
Queue<Event> events;
// The main thread calls the `Update` method continuously on each "frame"
void Update() {
// All accumulated events are processed in order and the queue is cleared
foreach (var event : events) Process(event);
events.Clear();
}
现在鉴于我的假设是正确的并且 TPL 使用SynchronizationContext.Current
,应用程序中的任何代码都应该能够执行如下操作:
async void Foo() {
someLabel.Text = "Processing";
await BackgroundTask();
// This has to execute on the main thread
someLabel.Text = "Done";
}
这让我想到了这个问题。我如何实施自定义SynchronizationContext
这将允许我在我自己的线程上处理延续?这是正确的方法吗?
实施定制SynchronizationContext
这不是世界上最容易的事情。我有一个开源的单线程实现here https://github.com/StephenCleary/AsyncEx.Context您可以将其用作起点(或者可能只是代替主循环使用)。
默认情况下,AsyncContext.Run
需要一个委托来执行并在完全完成时返回(因为AsyncContext
使用自定义SynchronizationContext
,它能够等待async void
方法以及常规异步/同步代码)。
AsyncContext.Run(async () => await DoSomethingAsync());
如果您想要更大的灵活性,您可以使用AsyncContext
高级成员(这些不会出现在 IntelliSense 中,但它们在那里)保持上下文活动,直到出现一些外部信号(如“退出帧”):
using (var context = new AsyncContext())
{
// Ensure the context doesn't exit until we say so.
context.SynchronizationContext.OperationStarted();
// TODO: set up the "exit frame" signal to call `context.SynchronizationContext.OperationCompleted()`
// (note that from within the context, you can alternatively call `SynchronizationContext.Current.OperationCompleted()`
// Optional: queue any work you want using `context.Factory`.
// Run the context; this only returns after all work queued to this context has completed and the "exit frame" signal is triggered.
context.Execute();
}
AsyncContext
's Run
and Execute
替换当前的SynchronizationContext
当它们运行时,但它们会保存原始上下文并在返回之前将其设置为当前上下文。这使得它们能够以嵌套方式(例如“框架”)很好地工作。
(我假设“框架”是指一种类似 WPF 的调度程序框架)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)