private void DetectCompletion(ProducerConsumerBuffer buffer, CancellationToken token)
        {
            while ((this.producers.Any(x => x.ProducerState != State.Completed) ||
                    (this.producerCount > this.consumerCount + this.discardConsumerCount &&
                     this.consumers.Any(x => x.ConsumerState != State.Completed))) &&
                   !token.IsCancellationRequested)
            {
                Thread.Sleep(1);
            }

            if (token.IsCancellationRequested)
            {
                this.logger?.LogInformation($"{this.LogName}: Completed execution by stop!");
            }
            else
            {
                foreach (var consumer in this.consumers)
                {
                    consumer.ConsumerState = State.Completed;
                    consumer.Finish();
                }

                this.logger?.LogInformation($"{this.LogName}: Completed execution as all producers and consumers has finished their job!");
            }

            this.OnCompleted?.Invoke(this, EventArgs.Empty);
        }
        private void LogPerformance(ProducerConsumerBuffer buffer, CancellationToken token)
        {
            int    sleep = this.Configuration.LogPerfomanceMs;
            double ratio = 1000.0 / sleep;

            long lastProducer, lastDiscardProducer,
                 lastConsumer, lastDiscardConsumer,
                 perProducer, perProducerDiscard,
                 perConsumer, perConsumerDiscard;

            while ((this.producers.Any(x => x.ProducerState != State.Completed) ||
                    (this.producerCount > this.consumerCount + this.discardConsumerCount &&
                     this.consumers.Any(x => x.ConsumerState != State.Completed))) &&
                   !token.IsCancellationRequested)
            {
                lastProducer        = this.producerCount;
                lastConsumer        = this.consumerCount;
                lastDiscardProducer = this.discardProducerCount;
                lastDiscardConsumer = this.discardConsumerCount;

                Thread.Sleep(sleep);
                perProducer        = (long)(ratio * (this.producerCount - lastProducer));
                perConsumer        = (long)(ratio * (this.consumerCount - lastConsumer));
                perProducerDiscard = (long)(ratio * (this.discardProducerCount - lastDiscardProducer));
                perConsumerDiscard = (long)(ratio * (this.discardConsumerCount - lastDiscardConsumer));

                this.logger.LogInformation($"{this.LogName}: Produce/s: {perProducer} Discard/s: {perProducerDiscard} Consume/s: {perConsumer} Discard/s: {perConsumerDiscard}  Buffer: {buffer.Count}");
            }
        }
 private void StartDetectCompletion(ProducerConsumerBuffer buffer, CancellationToken token)
 {
     new Thread(() => this.DetectCompletion(buffer, token))
     {
         IsBackground = true,
         Priority     = ThreadPriority.AboveNormal,
     }.Start();
 }
 private void StartLogPerformance(ProducerConsumerBuffer buffer, CancellationToken token)
 {
     if (this.logger != null && this.Configuration.LogPerfomance)
     {
         new Thread(() => this.LogPerformance(buffer, token))
         {
             IsBackground = true,
             Priority     = ThreadPriority.Lowest,
         }.Start();
     }
 }
 private async Task StartProducer(IProducer producer, ProducerConsumerBuffer buffer, CancellationToken token)
 {
     while (!token.IsCancellationRequested && producer.ProducerState != State.Completed)
     {
         if (await producer.Produce(token) is object item)
         {
             buffer.AddItem(item, token);
             Interlocked.Increment(ref this.producerCount);
         }
         else
         {
             Interlocked.Increment(ref this.discardProducerCount);
         }
     }
 }
        private async Task StartConsumer(IConsumer consumer, ProducerConsumerBuffer buffer, CancellationToken token)
        {
            while (!token.IsCancellationRequested && consumer.ConsumerState != State.Completed)
            {
                var item = await buffer.GetItem(token);

                if (consumer.CanConsume(item))
                {
                    consumer.Consume(item, token);
                    Interlocked.Increment(ref this.consumerCount);
                }
                else
                {
                    Interlocked.Increment(ref this.discardConsumerCount);
                }
            }
        }
        private void Schedule(CancellationToken token)
        {
            this.logger?.LogInformation($"{this.LogName}: Starting producers and consumers!");

            var buffer = new ProducerConsumerBuffer();

            this.StartDetectCompletion(buffer, token);
            this.StartLogPerformance(buffer, token);

            this.consumers
            .AsParallel()
            .Select(x => this.StartConsumer(x, buffer, token).ConfigureAwait(false))
            .ToList();

            this.producers
            .AsParallel()
            .Select(x => this.StartProducer(x, buffer, token).ConfigureAwait(false))
            .ToList();
        }