0

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();
    }

2 Answers2

0

The problem is that your producer will never complete:

// This will run until after CompleteAdding is called
while (!collection.IsAddingCompleted)
{
    var firstItem = list.FirstOrDefault();
    collection.TryAdd(firstItem);
    list.Remove(firstItem);
}
//... which doesn't happen until after the loop
collection.CompleteAdding();

It looks like you're just trying to add all of the items in your list, which should be as simple as:

Task producer = Task.Run(() =>
{
    if (list.Count > 0)
        Console.WriteLine("Block begin");
    while(list.Any())
    {
        var firstItem = list.First();
        collection.TryAdd(firstItem);
        list.Remove(firstItem);
    }
    collection.CompleteAdding();
});

Or, a simpler method:

Task producer = Task.Run(() =>
{
    if (list.Count > 0)
        Console.WriteLine("Block begin");
    foreach(var item in list)
    {
        collection.TryAdd(item);
    }
    list.Clear();
    collection.CompleteAdding();
});
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • The collection has the capacity of 4, the code can produce 4 items and consume 4 items, but it still doesn't reach the print part. Is there anything wrong such as `WaitAll' or `ContinueWith`? –  Aug 11 '14 at 21:01
  • @Love Not in what I'm seeing - you can always put breakpoints in and/or use the concurrency visualizer... – Reed Copsey Aug 11 '14 at 21:09
0

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;
    }