53

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.

roundcrisis
  • 17,276
  • 14
  • 60
  • 92
eyal
  • 551
  • 1
  • 4
  • 3

6 Answers6

15

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

Aaron Stainback
  • 3,489
  • 2
  • 31
  • 32
13

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.

Preet Sangha
  • 64,563
  • 18
  • 145
  • 216
  • Iterator blocks are no longer relevant as of Rx 2.0, which now provides overloads of `Observable.Create` that accept a `Task`-returning function, thus conflating the creation of an observable (call/cc) with C# 5's native coroutines (_async/await_). This can be defined as an "asynchronous iterator". See [this thread](http://social.msdn.microsoft.com/Forums/en-US/6e0ead42-4d89-429d-b6a8-d422cf673390/how-is-the-experimental-createiterator-redundant-with-net-45?forum=rx) for more info and [my blog post](http://davesexton.com/blog/post/async-iterators.aspx) for a comparison. – Dave Sexton Sep 16 '14 at 03:00
  • Hmm, ok thanks. I'm fairly new to SO and I wasn't sure about wiki etiquette. I'll read up on the "rules" first. – Dave Sexton Sep 16 '14 at 06:03
  • No worries - the bottom line is that if you can improve something then you should. – Preet Sangha Sep 16 '14 at 11:11
  • Well I changed it by adding an "Edit:" section below the original but apparently it was rejected? "This edit changes too much in the original post; the original meaning or intent of the post would be lost." I disagree that it changes the original intent, but aren't you really the only one who knows your "original intent"? – Dave Sexton Sep 16 '14 at 19:18
  • 1
    Sorry that wasn't me. So I suggest that you just add another answer if people keep reverting it. – Preet Sangha Sep 16 '14 at 22:23
  • 1
    No worries, sorry to bother you with OOB comments. Thanks for the tips. – Dave Sexton Sep 17 '14 at 00:27
6

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.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 3
    One limitation with using threads is that (in both Winforms and WPF, I believe) the controls of a form which was created by thread `Larry` cannot be accessed by thread `Moe`, even if thread `Larry` is not doing anything with the form and will be blocked until `Moe` isn't doing anything with it either. If two coroutines could run on the same OS thread, they could share resources which are otherwise only usable by the creating thread. – supercat May 20 '13 at 19:19
6

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.

noseratio
  • 59,932
  • 34
  • 208
  • 486
3

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.

Kind Contributor
  • 17,547
  • 6
  • 53
  • 70
  • 1
    This. We now have [native Channels in .NET](https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/) and I use them for [async coroutines](https://dev.to/noseratio/asynchronous-coroutines-with-c-8-0-and-iasyncenumerable-2e04). – noseratio Sep 10 '20 at 10:51
1

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;
Kendar
  • 692
  • 7
  • 25