这是可能的,并且基本上与 Microsoft 对 Windows 窗体和 WPF 同步上下文所做的事情相同。
第一部分 - 您位于 OpenGL 线程中,并且想要将一些工作放入线程池中,并且在该工作完成后您想要返回到 OpenGL 线程中。
我认为你解决这个问题的最佳方法是实施你自己的SynchronizationContext https://msdn.microsoft.com/library/system.threading.synchronizationcontext%28v=vs.110%29.aspx。这个东西基本上控制着如何TaskScheduler
工作原理以及它如何安排任务。默认实现只是将任务发送到线程池。你需要做的就是将任务发送到一个专用线程(保存OpenGL上下文)并在那里一一执行它们。
实现的关键是覆盖Post
和Send
方法。两种方法都应该执行回调,其中Send
必须等待通话结束Post
才不是。使用线程池的示例实现是Send
只需直接调用回调即可Post
将回调委托给线程池。
对于 OpenGL 线程的执行队列,我认为查询一个线程BlockingCollection https://msdn.microsoft.com/library/dd267312(v=vs.110).aspx应该做得很好。只需将回调发送到该队列即可。如果从错误的线程调用 post 方法并且您需要等待任务完成,您可能还需要一些回调。
但总而言之,这种方式应该可行。async
/await
确保SynchronizationContext
例如,在线程池中执行异步调用后恢复。因此,在将一些工作转移到另一个线程后,您应该能够返回到 OpenGL 线程。
第二部分 - 您位于另一个线程中,想要将一些工作发送到 OpenGL 线程中并等待该工作完成。
这也是有可能的。在这种情况下我的想法是你不使用Task
但其他等待的对象。一般来说,每个对象都可以等待的。它只需要实现一个公共方法getAwaiter()
返回一个实现的对象INotifyCompletion https://msdn.microsoft.com/library/system.runtime.compilerservices.inotifycompletion(v=VS.110).aspx界面。什么await
所做的就是将剩余的方法放入一个新的方法中Action
并将此操作发送到OnCompleted https://msdn.microsoft.com/library/system.runtime.compilerservices.inotifycompletion.oncompleted(v=vs.110).aspx该接口的方法。一旦等待的操作完成,等待者就应该调用预定的操作。该服务员还必须确保SynchronizationContext
被捕获并在捕获的上执行延续SynchronizationContext
。这听起来很复杂,但是一旦掌握了窍门,事情就会变得相当简单。对我帮助很大的是参考资料来源YieldAwaiter http://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/YieldAwaitable.cs,dfe9c1a07538b32f(这基本上是如果你使用的话会发生的情况await Task.Yield()
)。这不是您所需要的,但我认为这是一个起点。
返回等待者的方法必须负责将实际工作发送到必须执行它的线程(您可能已经拥有第一部分中的执行队列),并且等待者必须在该工作完成后触发。
结论
不犯错误。这是一项繁重的工作。但如果你做到了这一切,你就会遇到更少的问题,因为你可以无缝地使用async
/await
模式就好像您在 Windows 窗体或 WPF 中工作一样,这是一个色调加号。