En C#, estoy interesado en detener un bucle Parallel.ForEachAsync (considerando las diferencias entre Stop y Break ); para Parallel.ForEach puedo hacer lo siguiente:
Parallel.ForEach(items, (item, state) => { if (cancellationToken.IsCancellationRequested) { state.Stop(); return; } // some process on the item Process(item); }); Sin embargo, dado que tengo un proceso que debe ejecutarse de forma asincrónica, cambié a Parallel.ForEachAsync . ForEachAsync no tiene el método Stop() , puedo break el ciclo de la siguiente manera, pero me pregunto si esta es la forma más efectiva de romper el aspecto (en otras palabras, el ciclo debe detenerse lo antes posible cuando recibe la solicitud de cancelación).
await Parallel.ForEachAsync(items, async (item, state) => { if (cancellationToken.IsCancellationRequested) { return; } // some async process on the item await ProcessAsync(item); });Vas a necesitar algo como esto:
await Parallel.ForEachAsync(items, async (item, state) => { await ProcessAsync(item, cancellationToken); }); async Task ProcessAsync(string item, CancellationToken ct) { while (!ct.IsCancellationRequested) { //Process } }La lambda Parallel.ForEachAsync tiene un CancellationToken como su segundo parámetro. Este token lo proporciona la API, no es el mismo token que pasó en ParallelOptions . Puede reenviar este token a cualquier método asíncrono que invoque dentro de lambda. Si invoca métodos no cancelables, lo mejor que puede hacer es llamar a ThrowIfCancellationRequested en lugares estratégicos dentro de la lambda:
var cts = new CancellationTokenSource(); var options = new ParallelOptions() { CancellationToken = cts.Token }; try { await Parallel.ForEachAsync(items, options, async (item, ct) => { //... ct.ThrowIfCancellationRequested(); //... await ProcessAsync(item, ct); //... ct.ThrowIfCancellationRequested(); //... }); } catch (OperationCanceledException ex) { // ... }