/// <summary> /// Start consuming. /// </summary> public void Start() { // all tasks scheduled in a background thread Task.Run(() => { var empty = false; // used to handle cancellation requests var parallelOptions = new ParallelOptions { CancellationToken = _cts.Token }; try { // a parallel iteration on _workers. // workers work on different threads, or even different CPU cores. Parallel.ForEach(_workers, parallelOptions, worker => { // try dequeue a product TProduct raw; while (_queue.TryTake(out raw, _waitProducerMsTimeout)) { // cancel if stopped parallelOptions.CancellationToken.ThrowIfCancellationRequested(); // process data asynchronously var result = ConsumeElement(raw, worker); // handle results synchronously lock (this) { parallelOptions.CancellationToken.ThrowIfCancellationRequested(); // handle the result _syncResultHandleFunc(result); ConsumedCnt++; Update?.Invoke(result); if (result.IsSuccessful) { _continuousFailCnt = 0; } else { _continuousFailCnt++; if (_continuousFailCnt >= 10) { SourceInvalid?.Invoke(); return; } } if (ConsumedCnt >= TargetCnt.GetValueOrDefault(int.MaxValue)) { TargetAmountReached?.Invoke(); return; } } } empty = true; }); if (empty) { ProducerEmpty?.Invoke(); } } catch (OperationCanceledException) { // tasks stopped } }); }
/// <summary> /// Start consuming. /// </summary> public void Start() { Task.Run(() => { while (!_cancellationTokenSource.IsCancellationRequested) { Thread.Sleep(100); TProduct raw; if (!_queue.TryTake(out raw, _waitProducerTimeoutMs)) { ProducerEmpty?.Invoke(); break; } if (_cancellationTokenSource.IsCancellationRequested) { return; } var result = ConsumeElement(raw); Update?.Invoke(result); if (result.IsSuccessful) { _continuousFailCnt = 0; } else { _continuousFailCnt++; if (_continuousFailCnt >= 10) { SourceInvalid?.Invoke(); break; } } ConsumedCnt++; if (ConsumedCnt >= TargetCnt.GetValueOrDefault(int.MaxValue)) { TargetAmountReached?.Invoke(); } } }, _cancellationTokenSource.Token); }