我正在寻找在c#中实现协同例程(用户调度线程)的方法.使用c ++时我使用的是光纤.我在互联网上看到C#中不存在光纤.我想获得类似的功能.
有没有"正确"的方法在c#中实现协同程序?
我曾想过使用在调度程序线程上获取单个执行互斥锁+ 1的线程来实现它,该线程为每个协同程序释放这个互斥锁.但这似乎非常昂贵(它强制每个协程之间的上下文切换)
我也看过了yield迭代器的功能,但据我所知,你不能在内部函数中产生(仅在原始的ienumerator函数中).所以这对我有点好处.
使用yield关键字来实现这里所示的简单状态机是否可行.对我来说,看起来C#编译器为您完成了艰苦的工作,因为它在内部实现了状态机以使yield语句有效.
你可以在编译器已经完成的工作之上捎带并让它为你实现大部分状态机吗?
有没有人这样做,技术上可行吗?
我使用C#迭代器作为协同程序的替代品,它一直很好用.我想切换到async/await,因为我认为语法更清晰,它给了我类型安全. 在这篇(过时的)博客文章中,Jon Skeet展示了实现它的可能方式.
我选择采用略有不同的方式(通过实现我自己SynchronizationContext和使用Task.Yield).这很好.
然后我意识到会有问题; 目前协程不必完成运行.它可以在任何产生的点上优雅地停止.我们可能有这样的代码:
private IEnumerator Sleep(int milliseconds)
{
Stopwatch timer = Stopwatch.StartNew();
do
{
yield return null;
}
while (timer.ElapsedMilliseconds < milliseconds);
}
private IEnumerator CoroutineMain()
{
try
{
// Do something that runs over several frames
yield return Coroutine.Sleep(5000);
}
finally
{
Log("Coroutine finished, either after 5 seconds, or because it was stopped");
}
}
Run Code Online (Sandbox Code Playgroud)
协程通过跟踪堆栈中的所有枚举器来工作.C#编译器生成一个Dispose函数,可以调用该函数以确保正确调用'finally'块CoroutineMain,即使枚举未完成.这样我们就可以优雅地停止协程,并通过调用堆栈Dispose上的所有IEnumerator对象来确保最终调用块.这基本上是手动展开.
当我用async/await编写我的实现时,我意识到我们会失去这个功能,除非我弄错了.然后我查找了其他协同解决方案,看起来Jon Skeet的版本看起来也没有任何处理方式.
我能想到处理这个问题的唯一方法就是拥有我们自己的自定义'Yield'函数,它会检查协程是否被停止,然后引发一个表明这个的异常.这将传播,执行finally块,然后被捕获到根附近的某处.我不觉得这很漂亮,因为第三方代码可能会捕获异常.
我误解了什么,这是否可以更容易地做到?或者我需要以异常方式执行此操作吗?
编辑:已请求更多信息/代码,所以这里有一些.我可以保证这只会在一个线程上运行,所以这里没有涉及线程.我们当前的协程实现看起来有点像这样(这是简化的,但它适用于这个简单的情况):
public sealed …Run Code Online (Sandbox Code Playgroud)