Search code examples
c#.net-4.0producer-consumerblockingcollection

Finish two tasks then printing something


I have three tasks, one is producer, then consumer and the last one is to print something after finishing the first two. However the code doesn't reach the last task, which means no printing.

 while (true)
            {
                ThreadEvent.WaitOne(waitingTime, false);

                lock (SyncVar)
                {
                    collection = new BlockingCollection<string>(4);
                    Task producer = Task.Run(() =>
                     {
                         if (list.Count > 0)
                             Console.WriteLine("Block begin");
                         while (!collection.IsAddingCompleted)
                         {
                             var firstItem = list.FirstOrDefault();
                             collection.TryAdd(firstItem);
                             list.Remove(firstItem);
                         }
                         collection.CompleteAdding();
                     });
                    Task consumer = Task.Run(() => DoConsume());
                    Task endTask = consumer.ContinueWith(i => Console.WriteLine("Block end"));// not print this line, why?
                    Task.WaitAll(producer, consumer, endTask);
                    if (ThreadState != State.Running) break;
                }
            }

Please look at my code logic.

EDIT:

For `DoConsume', it is complicated.

public void DoConsume()
    {
        if (collection.Count > 0)
            Console.WriteLine("There are {0} channels to be processed.", collection.Count);

        var workItemBlock = new ActionBlock<string>(
        workItem =>
        {
            bool result =ProcessEachChannel(workItem);
        });

        foreach (var workItem in collection.GetConsumingEnumerable())
        {
             workItemBlock.Post(workItem);
        }

        workItemBlock.Complete();
    }

Solution

  • I used Reed Copsey's code but the error is still there. Just can't figure it out why. I think that my code has the flaw at while (!collection.IsAddingCompleted).

    Because the collection has the boundary of 4, suppose there are two item left in the collection. The condition collection.IsAddingCompleted is never met therefore the code could not jump out of the while loop.

    I rewrote the code, it seems fine. The code is similar MSDN. I used Take to retrieve the element in the collection.

    while (true)
    {
        ThreadEvent.WaitOne(waitingTime, false);
    
        lock (SyncVar)
        {
            collection = new BlockingCollection<string>(4);
            DoWork dc = new DoWork();
            Task consumer = Task.Run(() =>
            {
                while (!collection.IsCompleted)
                {
                    string data = "";
                    try
                    {
                        if (collection.Count > 0)
                            data = collection.Take();
                    }
                    catch (InvalidOperationException e)
                    {
                        Console.WriteLine(e.Message);
                    }
                    if (data != "")
                    {
                        bool result = dc.DoConsume(data);
                    }
                }
            });
    
            Task producer = Task.Run(() =>
             {
                 if (list.Count > 0)
                     Console.WriteLine("Block begin");
                 foreach (var item in list)
                 {
                     collection.Add(item);
                 }
                 list.Clear();
    
                 collection.CompleteAdding();
             });
            Task endTask = consumer.ContinueWith(i => Console.WriteLine("Block end"));
            Task.WaitAll(producer, consumer, endTask);
            if (ThreadState != State.Running) break;
        }