Dasync / AsyncEnumerable

Defines IAsyncEnumerable, IAsyncEnumerator, ForEachAsync(), ParallelForEachAsync(), and other useful stuff to use with async-await
MIT License
443 stars 56 forks source link

proposal: throw exception if Yield.ReturnAsync called before it's ready #32

Open zivkan opened 5 years ago

zivkan commented 5 years ago

A user of your library posted this question on stack overflow.

Basically, they're using AsyncEnumerable<T>.ForEachAsync, but rather than producing items serially, they tried to produce concurrently by creating multiple concurrent generator tasks within the delegate that's passed into the AsyncEnumerable constructor. In other words, they were trying to use this library for the multi producer, single consumer pattern.

The problem is that the incorrect usage did not result in an error, but instead their program hung, and they didn't understand why. I believe it's related to calling Yield.ReturnAsync multiple times, before the previous task returned by ReturnAsync is in a completed state, causing the last call to overwrite information from the previous calls.

I believe if Yield.ReturnAsync throws an InvalidOperationException when it detects a call when the previous call is not completed, then it would allow your users to more quickly detect problems in their own code.

kind-serge commented 5 years ago

@zivkan, thanks for the suggestion. You are absolutely right about the sequential producer pattern. This might not be obvious to everyone, however, there are many more standard types that don't support concurrent programming model - take Dictionary<,> for example.

It is possible to add protection from parallel produce attempts, but it comes with a performance cost that would impact the majority of normal cases. On the other hand, we can update the documentation to explicitly call out this point.

What are your thoughts?

kind-serge commented 5 years ago

P.S. I replied to the SO question as well: https://stackoverflow.com/questions/54402544/c-sharp-asyncenumerable-running-awaiting-multiple-tasks-never-finishes/56189780#56189780 I've been thinking about adding a helper method for such scenarios with concurrent producers/consumers.

zivkan commented 5 years ago

I completely understand the performance concern. I think a helper method sounds best, but docs are good too. But honestly, I don't use your library, I was just trying to help out after investigating an interesting Stack Overflow question :)