public static IEnumerable <T> ConstantThreadedBuffer <T>(this IEnumerable <T> seq, int maxBatches, int batchSize) { BatchBlockingCollection <T> buffer = new BatchBlockingCollection <T>(maxBatches, batchSize); CancellationTokenSource cancel = new CancellationTokenSource(); var runner = Task.Run(() => { foreach (var t in seq) { if (cancel.IsCancellationRequested) { break; } buffer.Add(t, cancel.Token); } buffer.Complete(); }); void cancelAndWait() { cancel.Cancel(); runner.Wait(); } using (new DisposeAction(cancelAndWait)) { foreach (var t in buffer) { yield return(t); } } }
public static IEnumerable <T> TaskedThreadedBuffer <T>(this IEnumerable <T> seq, int maxBatches, int batchSize) { BatchBlockingCollection <T> buffer = new BatchBlockingCollection <T>(batchSize); Task readerTask = null; bool completed = false; var en = seq.GetEnumerator(); CancellationTokenSource cancel = new CancellationTokenSource(); Exception e = null; Action runReader = () => { if (readerTask != null) { readerTask.GetAwaiter().GetResult(); } if (completed) { return; } readerTask = Task.Run(() => { while (buffer.BatchCount < maxBatches) { if (cancel.IsCancellationRequested) { break; } try { if (!en.MoveNext()) { completed = true; buffer.Complete(); return; } buffer.Add(en.Current, cancel.Token); } catch (Exception ex) { buffer.Complete(); cancel.Cancel(); e = ex; } } readerTask = null; }); }; runReader(); void cancelAndWait() { cancel.Cancel(); buffer.Complete(); readerTask?.Wait(); } using (new DisposeAction(cancelAndWait)) { foreach (var t in buffer) { yield return(t); if (buffer.BatchCount < maxBatches / 2 || buffer.BatchCount == 0) { if (!completed) { runReader(); } } } if (e != null) { throw e; } } }