C# 中的协程

发布于 2024-08-28 06:56:03 字数 287 浏览 13 评论 0原文

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

在 C# 中是否有任何“正确”的方法来实现协程?

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

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

I am looking at ways to implement co-routines (user scheduled threads) in c#. When using c++ I was using fibers. I see on the internet fibers do not exist in C#. I would like to get similar functionality.

Is there any "right" way to implement coroutines in c#?

I have thought of implementing this using threads that acquire a single execution mutex + 1 on scheduler thread which releases this mutex for each coroutine. But this seems very costly (it forces a context switch between each coroutine)

I have also seen the yield iterator functionality, but as I understand you can't yield within an internal function (only in the original ienumerator function). So this does me little good.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

柠檬 2024-09-04 06:56:03

我相信新的 .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 的异步 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

I believe with the new .NET 4.5\C# 5 the async\await pattern should meet your needs.

async Task<string> DownloadDocument(Uri uri)
{  
  var webClient = new WebClient();
  var doc = await webClient.DownloadStringTaskAsync(url);
  // do some more async work  
  return doc;  
}  

I suggest looking at http://channel9.msdn.com/Events/TechEd/Australia/Tech-Ed-Australia-2011/DEV411 for more info. It is a great presentation.

Also http://msdn.microsoft.com/en-us/vstudio/gg316360 has some great information.

If you are using an older version of .NET there is a Async CTP available for older .NET with a go live license so you can use it in production environments. Here is a link to the CTP http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

If you don't like either of the above options I believe you could follow the async iterator pattern as outlined here. http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

变身佩奇 2024-09-04 06:56:03

编辑:您现在可以使用这些:.net 中有 Fiber api 吗?< /a>

我相信您应该查看 .NET 的反应式扩展< /a>.例如协程可以使用迭代器和yield语句进行模拟。

不过,您可能也想阅读这个SO问题

Edit: You can now use these: Is there a fiber api in .net?

I believe that you should look at the the Reactive Extensions for .NET. For example coroutines can be simulated using iterators and the yield statement.

However you may want to read this SO question too.

这样的小城市 2024-09-04 06:56:03

这里是一个使用线程实现协程的示例:

所以我作弊了。我使用线程,但我只
一次让其中一个运行。当我
创建一个协程,我创建一个线程,
然后握手结束
调用 Monitor.Wait(),
阻塞协程线程——它不会
继续运行,直到解锁为止。什么时候
是时候调用协程了,
我进行了一次交接,以
调用线程被阻塞,并且
协程线程可运行。同种
返回途中的交接。

这些交接有点昂贵,
与其他实现相比。
如果你需要速度,你会想要
编写你自己的状态机,并且
避免所有这些上下文切换。 (或者
你会想要使用光纤感知
运行时——切换光纤很漂亮
便宜。)但如果你想要表现力
代码,我认为协程持有一些
承诺。

Here is an example of using threads to implement coroutines:

So I cheat. I use threads, but I only
let one of them run at a time. When I
create a coroutine, I create a thread,
and then do some handshaking that ends
with a call to Monitor.Wait(), which
blocks the coroutine thread — it won’t
run anymore until it’s unblocked. When
it’s time to call into the coroutine,
I do a handoff that ends with the
calling thread blocked, and the
coroutine thread runnable. Same kind
of handoff on the way back.

Those handoffs are kind of expensive,
compared with other implementations.
If you need speed, you’ll want to
write your own state machine, and
avoid all this context switching. (Or
you’ll want to use a fiber-aware
runtime — switching fibers is pretty
cheap.) But if you want expressive
code, I think coroutines hold some
promise.

天涯沦落人 2024-09-04 06:56:03

It's 2020, lots of things have evolved in C#. I've published an article on this topic, Asynchronous coroutines with C# 8.0 and IAsyncEnumerable:

In the C# world, they (coroutines) have been popularized by Unity
game development
platform
, and Unity
uses
IEnumerator-style
methods and yield return for that.

Prior to C# 8, it wasn't possible to combine await and yield return within the same method, making it difficult to use asynchrony
inside coroutines. Now, with the compiler's support for
IAsyncEnumerable, it can be done naturally.

悲凉≈ 2024-09-04 06:56:03

通道缺失的部分

管道是 golang 中相对于通道而言缺失的部分。通道实际上是 golang 的核心。通道是核心并发工具。如果您在 C# 中使用类似协程的东西,但使用线程同步原语(信号量、监视器、互锁等),那么它是不一样的。

几乎相同 - Pipelines,但在

8 年后才引入,并且 .Net Standard(.Net Framework / .Net Core)支持 Pipelines [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)应将数据放入管道中,另一个线程应从管道中异步读取数据。许多管道可以串联用于字节流上的各种处理。 Pipeline 有一个读取器和一个写入器游标,虚拟缓冲区大小会导致写入器产生背压,以减少不必要的缓冲内存使用,通常会减慢网络流量。

Pipelines 和 Go Channels 之间存在一些关键差异。管道与 golang Channel 不同。管道是关于传递可变字节的,而不是用于使用内存引用(包括指针)发出信号的 golang 通道。最后,管道没有等效的select

(管道使用 Spans [https://adamsitnik.com/Span/],它已经存在了有一段时间,但现在在 .Net Core Spans 中进行了深度优化,显着提高了性能。.Net core 支持进一步提高了性能,但只是增量式的,因此 .Net Framework 的使用完全没问题),

因此管道应该是一个内置标准 。帮助替换.Net中的golang通道,但它们并不相同,并且在很多情况下管道不是答案。

Golang Channel 的直接实现

您需要小心(与 golang 一样),通过 .Net 通道传递的消息表明对象所有权的更改。这是只有程序员才能跟踪和检查的东西,如果你弄错了,你将有两个或多个线程在不同步的情况下访问数据。

Channels the missing piece

Pipelines are the missing piece relative to channels in golang. Channels are actually what make golang tick. Channels are the core concurrency tool. If you're using something like a coroutine in C# but using thread synchronisation primatives (semaphore, monitor, interlocked, etc..) then it's not the same.

Almost the same - Pipelines, but baked in

8 years later, and .Net Standard (.Net Framework / .Net Core) has support for Pipelines [https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/]. Pipelines are preferred for network processing. Aspcore now rates among the top 11 plaintext throughput request rates [https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext].

Microsoft advises best practice for interfacing with network traffic: the awaited network bytes (Completion Port IO) should put data into a pipeline, and another thread should read data from the pipeline asynchronously. Many pipelines can be used in series for various processes on the byte stream. The Pipeline has a reader and a writer cursor, and the virtual buffer size will cause backpressure on the writer to reduce unnecessary use of memory for buffering, typically slowing down network traffic.

There are some critical differences between Pipelines and Go Channels. Pipelines aren't the same as a golang Channel. Pipelines are about passing mutable bytes rather than golang channels which are for signalling with memory references (including pointers). Finally, there is no equivalent select with Pipelines.

(Pipelines use Spans [https://adamsitnik.com/Span/], which have been around for a little while, but now are optimised deeply in .Net Core. Spans improve performance significantly. The .Net core support improves performance further but only incrementally, so .Net Framework use is perfectly fine. )

So pipelines are a built-in standard should help replace golang channels in .Net, but they are not the same, and there will be plenty of cases where pipelines are not the answer.

Direct Implementations of Golang Channel

You would need to be careful (as with golang) that passed messages via a .Net Channel indicate a change of ownership over an object. This is something only a programmer can track and check, and if you get it wrong, you'll two or more threads accessing data without synchronisation.

清泪尽 2024-09-04 06:56:03

您可能对这是一个隐藏协程用法的库感兴趣。例如读取文件:

//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;

You may be interested in this is a library that hides the usage of coroutines. For example to read a file:

//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();

This library uses one thread to run all coroutines and allow calling the task for the truly asynchronous operations. For example to call another method as a coroutine (aka yielding for its return

//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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文