我正在研究在c#中实现协同例程(用户调度线程)的方法。使用c ++时我使用的是光纤。我在互联网上看到C#中不存在光纤。我想获得类似的功能。
在c#中有没有“正确”的方法来实现协同程序?
我想过使用在调度程序线程上获取单个执行互斥锁+ 1的线程来实现它,该线程为每个协同程序释放这个互斥锁。但这似乎非常昂贵(它强制在每个协程之间切换上下文)
我也看过了yield迭代器的功能,但据我所知,你不能在内部函数中产生(仅在原始的ienumerator函数中)。所以这对我没那么好。
答案 0 :(得分:12)
修改:您现在可以使用这些:Is there a fiber api in .net?
我相信你应该看看Reactive Extensions for .NET。例如coroutines can be simulated using iterators and the yield语句。
但您也可以阅读此SO question。
答案 1 :(得分:9)
我相信使用新的.NET 4.5 \ C#5,async \ await模式可以满足您的需求。
async Task<string> DownloadDocument(Uri uri)
{
var webClient = new WebClient();
var doc = await webClient.DownloadStringTaskAsync(url);
// do some more async work
return doc;
}
我建议您查看http://channel9.msdn.com/Events/TechEd/Australia/Tech-Ed-Australia-2011/DEV411以获取更多信息。这是一个很棒的演讲。
http://msdn.microsoft.com/en-us/vstudio/gg316360也有一些很好的信息。
如果您使用的是旧版本的.NET,则可以使用旧版.NET的Async CTP以及实时许可证,以便您可以在生产环境中使用它。以下是CTP http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983
的链接如果你不喜欢上述任何一个选项,我相信你可以遵循这里概述的异步迭代器模式。 http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983
答案 2 :(得分:6)
Here是使用线程实现协程的一个例子:
所以我作弊。我使用线程,但我只是 让其中一个一次运行。当我 创建一个协程,我创建一个线程, 然后做一些结束的握手 调用Monitor.Wait(),其中 阻止协程线程 - 它不会 再次运行,直到它被解锁。什么时候 是时候打电话给协程了, 我做了一个以结尾的传递 调用线程被阻止了,而且 coroutine thread runnable。同种 在返回途中的交接。
那些交接费用很贵, 与其他实现相比。 如果你需要速度,你会想要 编写自己的状态机,并且 避免所有这种上下文切换。 (要么 你会想要使用纤维感知 运行时 - 交换光纤很漂亮 便宜。)但如果你想表达 代码,我认为协同程序有一些 的承诺。
答案 3 :(得分:1)
到2020年,C#发生了许多变化。我已经发表了有关该主题的文章Asynchronous coroutines with C# 8.0 and IAsyncEnumerable:
在C#世界中,它们(协程)已由Unity game development platform和Unity进行了普及。 用途
IEnumerator
样式 方法和yield return
。在C#8之前,不可能在同一方法中组合
await
和yield return
,这使得使用异步变得困难 内协程。现在,有了编译器的支持IAsyncEnumerable
,这可以自然完成。
答案 4 :(得分:0)
您可能感兴趣this是一个隐藏协同程序使用的库。例如,要读取文件:
//Prepare the file stream
FileStream sourceStream = File.Open("myFile.bin", FileMode.OpenOrCreate);
sourceStream.Seek(0, SeekOrigin.End);
//Invoke the task
yield return InvokeTaskAndWait(sourceStream.WriteAsync(result, 0, result.Length));
//Close the stream
sourceStream.Close();
这个库使用一个线程来运行所有协同程序,并允许调用任务进行真正的异步操作。例如,将另一个方法称为协程(又称为返回
)//Given the signature
//IEnumerable<string> ReadText(string path);
var result = new Container();
yield return InvokeLocalAndWait(() => _globalPathProvider.ReadText(path), container);
var data = container.RawData as string;
答案 5 :(得分:0)
引导缺少的作品
相对于golang中的渠道,管道是缺少的部分。通道实际上是使golang滴答作响的原因。通道是核心并发工具。如果您在C#中使用的是协程,但使用线程同步基元(信号量,监视器,互锁等),则不一样。
几乎相同-管道,但在其中烘烤
8年后,.Net标准(.Net Framework / .Net Core)支持管道[https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/]。首选管道进行网络处理。 Aspcore现在排名前11位的纯文本吞吐量请求速率[https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext]中。
Microsoft建议与网络流量进行交互的最佳做法:等待的网络字节(完成端口IO)应将数据放入管道,而另一个线程应异步从管道读取数据。可以将许多管线串联用于字节流上的各种过程。管道具有读取器和写入器游标,并且虚拟缓冲区的大小将对写入器造成反压,以减少不必要的内存缓冲使用,通常会减慢网络流量。
管道和执行通道之间存在一些关键差异。管道与golang频道不同。管道与传递可变字节有关,而不是与用于通过内存引用(包括指针)发出信号的golang通道有关。最后,管道没有等效的select
。
(管道使用Spans [https://adamsitnik.com/Span/],这种方法已经存在了一段时间,但是现在在.Net Core中进行了优化。Spans显着提高了性能。.NetCore支持进一步提高了性能,但只是逐步提高了性能,因此,使用.Net Framework完全可以。)
因此,管道是内置标准,应该有助于替换.Net中的golang通道,但它们并不相同,并且在很多情况下管道不能解决问题。
Golang频道的直接实现
您需要格外小心(与golang一样),即通过.Net通道传递的消息表明对象的所有权发生了变化。只有程序员才能跟踪和检查,如果您弄错了,您将有两个或多个线程在不同步的情况下访问数据。