0

Running this program will chew up 25% CPU power in a four cores system. So basically something is running at full thrust. I narrowed it down to the consumer however the load doesn't stop on pressing "x" which should terminate my consumers.

my code

internal class TestBlockingCollectionConsumerProducer2
{
    private int _itemCount;

    internal void Run()
    {
        BlockingCollection<string> blockingCollection = new BlockingCollection<string>();

        // The token source for issuing the cancelation request.
        CancellationTokenSource cts = new CancellationTokenSource();

        // Simple thread waiting for a Console 'x'
        Task.Factory.StartNew(() =>
        {
            if (Console.ReadKey().KeyChar == 'x')
            {
                cts.Cancel();
            }
        });

        // start producer
        Task.Factory.StartNew(() => Produce(blockingCollection, cts.Token));

        // start multiple consumers
        const int THREAD_COUNT = 5;
        for (int i = 0; i < THREAD_COUNT; i++)
        {
            Task.Factory.StartNew(() => Consume(blockingCollection, cts.Token));
        }

        while (true);
    }

    private void Produce(BlockingCollection<string> blockingCollection, CancellationToken cancellationToken)
    {
        while (true)
        {
            for (int i = 0; i < 10; i++)
            {
                blockingCollection.Add(string.Format("Item {0}", _itemCount++), cancellationToken);
            }

            Console.WriteLine("Added 10 items. Current queue length:" + blockingCollection.Count);
            Thread.Sleep(10000);
        }
    }

    private void Consume(BlockingCollection<string> blockingCollection, CancellationToken cancellationToken)
    {
        try
        {
            foreach (string item in blockingCollection.GetConsumingEnumerable(cancellationToken))
            {
                Console.WriteLine(string.Format("[{0}] Consumer: Consuming: {1}", Thread.CurrentThread.ManagedThreadId, item));
                Thread.Sleep(2500);
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("[{0}] Consumer: Operation has been canceled.", Thread.CurrentThread.ManagedThreadId);
        }
    }
}

My question is:
1. Why is the CPU load so high? Shouldn't GetConsumingEnumerable() block and therefore use no CPU time at all?
2. Why doesn't it stop on cts.Cancel()?

amdixon
  • 3,814
  • 8
  • 25
  • 34
lapsus
  • 2,915
  • 2
  • 32
  • 61

1 Answers1

5

Problem isn't with the BlockingCollection.

It is the infinite loop with while (true);. What this is doing in Run method? That's what burning your cpu.

I see Produce method doesn't respect the CancellationToken. Instead of infinite loop, you should be using while (!cancellationToken.IsCancellationRequested).

Also, for cts.Cancel it indeed cancels the operation. If that doesn't works for some reason, please provide small but complete program which reproduces the problem.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • How else would I keep the application alive? The Run() is called from Main() - nothing special there. – lapsus Apr 23 '15 at 09:45
  • @lapsus see: Task.WaitAll – Andrei Tătar Apr 23 '15 at 09:47
  • @lapsus It depends upon how long you wanted to keep the application alive. – Sriram Sakthivel Apr 23 '15 at 09:47
  • @lapsus : so you're keeping the application alive by making it run an infinite loop, then you're surprised the CPU is used? Now I'm confused. If you are in a Console application, `Console.ReadLine()` will block, make you control exactly when you shut down the app, and **will not use a full processor by itself** – Falanwe Apr 23 '15 at 09:51
  • Ok I see. The program should run as a service - so I don't want it to ever stop. That's why I though while(true) might be a good idea. I should probably put this into another question. – lapsus Apr 23 '15 at 09:51
  • @lapsus I guess you could use `Console.ReadKey()`? – default Apr 23 '15 at 09:51
  • Yes Console.Readxxx() of course works. I should have said it should run as a service earlier... I need to look into windows service hosting options. Thanks. – lapsus Apr 23 '15 at 09:56
  • Calling `Console.Readxxx` from multiple threads is problematic. If can wait on a waithandle that is never signaled. Or just use `Thread.CurrentThread.Join();` – Sriram Sakthivel Apr 23 '15 at 09:57