C#中的协同程序

时间:2010-04-08 11:20:51

标签: c# coroutine fiber

我正在研究在c#中实现协同例程(用户调度线程)的方法。使用c ++时我使用的是光纤。我在互联网上看到C#中不存在光纤。我想获得类似的功能。

在c#中有没有“正确”的方法来实现协同程序?

我想过使用在调度程序线程上获取单个执行互斥锁+ 1的线程来实现它,该线程为每个协同程序释放这个互斥锁。但这似乎非常昂贵(它强制在每个协程之间切换上下文)

我也看过了yield迭代器的功能,但据我所知,你不能在内部函数中产生(仅在原始的ienumerator函数中)。所以这对我没那么好。

6 个答案:

答案 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之前,不可能在同一方法中组合awaityield 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通道传递的消息表明对象的所有权发生了变化。只有程序员才能跟踪和检查,如果您弄错了,您将有两个或多个线程在不同步的情况下访问数据。