Created
October 7, 2020 09:31
-
-
Save wi7a1ian/d0dcced7d47d3f88e6cd98a6539945e3 to your computer and use it in GitHub Desktop.
Explanation of `EnumeratorCancellation` in async enumerable #async #csharp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Program | |
{ | |
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8 | |
public static async Task Main(string[] args) | |
{ | |
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(300)); | |
/* | |
* Passing the token directly to the method is easier, but it doesn’t work when you’re handed an arbitrary IAsyncEnumerable<T> | |
* from some other source but still want to be able to request cancellation of everything that composes it. | |
* In corner-cases, it can also be advantageous to pass the token to GetAsyncEnumerator, as doing so avoids “burning in” the token | |
* in the case where the single enumerable will be enumerated multiple times: By passing it to GetAsyncEnumerator, | |
* a different token can be passed each time. Of course, it’s also possible that two different tokens end up getting passed into the same iterator, | |
* one as an argument to the iterator and one via GetAsyncEnumerator. In that case, the compiler-generated code handles this | |
* by creating a new linked token that will have cancellation requested when either of the two tokens has cancellation requested, | |
* and that new “combined” token will be the one the iterator body sees. | |
*/ | |
//await foreach (int item in RangeAsync(0, 5, cts.Token)) // same as.. | |
await foreach (int item in RangeAsync(0, 5).WithCancellation(cts.Token)) // but different than... | |
//await foreach (int item in RangeAsync(0, 5, cts.Token).WithCancellation(cts.Token)) | |
{ | |
Console.Write(item + " "); | |
} | |
// the only way to cancel async enumerable when its provided by another method | |
await foreach (int item in RangeAsyncWrapper().WithCancellation(cts.Token)) | |
{ | |
Console.Write(item + " "); | |
} | |
} | |
private static async IAsyncEnumerable<int> RangeAsync(int start, int count, | |
[EnumeratorCancellation] CancellationToken ct = default) | |
{ | |
for (int i = 0; i < count; i++) | |
{ | |
await Task.Delay(100, ct); | |
yield return start + i; | |
} | |
} | |
private static IAsyncEnumerable<int> RangeAsyncWrapper() | |
=> RangeAsync(0, 5); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment