13

I'm trying to run a Parallel.ForEachAsync(), but I am getting these two errors:

Error 1:
Argument 2: can not convert from System.Threading.Tasks.ParallelOptions to System.Threading.CancellationToken
Error 2:
Delegate Func<WC, CancellationToken, ValueTask> does not take 1 argument

And this is my code:

public async Task<List<DisplayDto>> GetData()
{
    var options = new ParallelOptions()
    {
        MaxDegreeOfParallelism = 20;
    };

    await Parallel.ForEachAsync(result, options, async OrderNumber => {
        //Do Stuff here. 
    });
}

What must be altered in order for this to work as I want?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Doctor Ford
  • 440
  • 1
  • 5
  • 17

1 Answers1

24

Parallel.ForEachAsync expects Func<TSource,CancellationToken,ValueTask> i.e. accepting 2 parameters (first one being an element of collection and second one - CancellationToken), not one. Usage can look like that:

public async Task<List<DisplayDto>> GetData()
{
    var options = new ParallelOptions()
    {
        MaxDegreeOfParallelism = 20
    };

    await Parallel.ForEachAsync(result, options, async (OrderNumber, ct) => {
        // do not forget to use CancellationToken (ct) where appropriate 
        // Do Stuff here. 
    });
 }

Example.

Also can be useful - The need for two cancellation tokens in .NET 6 Parallel.ForEachAsync?

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • and the `ct` in `async (OrderNumber, ct)` is the `CancellationToken`? – Doctor Ford Dec 06 '21 at 17:37
  • 1
    @DoctorFord yes. – Guru Stron Dec 06 '21 at 17:39
  • 1
    There is an issue with your code - you can't actually use (ct) since you never provided it. You need to pass the cancellation token in like this `Parallel.ForEachAsync(result, myCancelToken, async (OrderNumber, ct) => { });` Or, it appears you can also assign your token to the `CancellationToken` property of the `ParallelOptions` – snow_FFFFFF Feb 22 '22 at 20:06
  • @snow_FFFFFF _"you can't actually use (ct) since you never provided it."_ - it depends on what do you mean/understand by "use". What I meant by use is to analyze it's canceled state - in this case you actually can cause `Parallel.ForEachAsync` will provide one if programmer has not (there are situations when `ForEachAsync` implementation will cancel it, one that I know of - one of the tasks will result in exception). _"You need to pass the cancellation token"_ as I explained - no, it is not required. – Guru Stron Feb 23 '22 at 07:46
  • The CancellationToken certainly isn't required. My comment was related to your comment about checking `ct`. I'm not clear how it will receive a cancellation request without being associated with a source of cancellation. Above, it seems like `ct` is nothing more than a placeholder in the function signature. I think your post is helpful, I just think it could be more clear about how to provide a useful CancellationToken. I've never seen anything in the documentation to indicate that there would be a source of cancellation in your sample. – snow_FFFFFF Feb 23 '22 at 16:53
  • @snow_FFFFFF once again - it is not just a placeholder. If user does not provide token - `ForEachAsync` internally will create a token source and will pass token to the handler function (i.e. `ct` will be some meaningful token from source created by implementation). If any of the handler functions fails with exception this source, controlled by `ForEachAsync` implementation will cancel and I think it is quite useful to use and check `ct` for "fail fast" purposes. [Check out](https://dotnetfiddle.net/hKOMU7) yourself. – Guru Stron Feb 23 '22 at 17:16
  • 2
    Thanks for the sample - my first reaction was that nothing was actually using the token, but I do see that the exception does cause it to indicate cancellation. This certainly confirms that the MS documentation isn't clear to me, but hopefully this string of comments will be helpful to someone else :) – snow_FFFFFF Feb 24 '22 at 15:53