多个任务如何将值添加到并发队列

时间:2016-09-17 14:49:04

标签: c# .net

我正在努力将多个任务添加到并发队列的integeres。我看过一些教程,但我发现它们非常复杂。另外,我想这样做是因为我想实现消费者 - 生产者问题。

这就是我一直想做的事情

class Program
    {
        const int MAX_VALUE = 20;
        const int valueP = 0;
         static BlockingCollection<int> bc = new BlockingCollection<int>(new ConcurrentQueue<int>(), 20 );

        static void producer(int value)
        {

            for (int i = 0; i < MAX_VALUE; i++)
            {
                bc.Add(value);

                value++;
                Console.WriteLine( value);
                Thread.Sleep(2000);
            }
            Thread.Sleep(2000);
        }
        static void consuemr()
        {
            int item = 0;
            item = bc.Take();
            Console.WriteLine("An item has been consumed", item);
            Thread.Sleep(2000);
        }
        static void Main(string[] args)
        {
            Task t1 =Task.Run(() => producer(valueP));
            Task t2 = Task.Run(() => producer(valueP));

            Task.WaitAll(t1, t2);
            Task.Run(() =>consuemr());


        }
       }

但是当它引领消费者时,事情就会停止工作

1 个答案:

答案 0 :(得分:1)

根据BlockingCollection的指定构造函数,您可以拥有20个项目的限制。

你输入了40个项目,所以在添加了20个项目之后,方法将被阻止。

尝试将构造函数更改为

static BlockingCollection<int> bc = new BlockingCollection<int>(new ConcurrentQueue<int>(), 100);

然后你会发现它会起作用。

要解决此问题,您需要确保消费者在生产者添加物品时正在取物品。

由于你对生产者做了Task.WaitAll,它会等到所有项目都被添加(这种情况永远不会发生,因为在添加了20个项目后,下一次调用Add()方法会阻塞,请参阅备注https://msdn.microsoft.com/en-us/library/dd287137(v=vs.110).aspx)部分。

您需要重新设计代码,以便消费者在生产者添加项目时使用项目,或确保BlockingCollection的上限大于您要添加的项目数在服用它们之前。

除了您当前的代码,请替换最后一行

Task.Run(() =>consuemr());

Task.Run(() =>consuemr()).Wait();

我在非生产代码中使用.Wait()或.WaitAll()不是问题,但在生产代码中你最好使用async ..等待。

完整示例

我希望我的提示能给你一些如何解决的想法。试试这个:

class Program
{
    const int MAX_VALUE = 20;
    const int valueP = 0;
    static BlockingCollection<int> bc = new BlockingCollection<int>(new ConcurrentQueue<int>(), 20);

    static void producer(int value)
    {
        for (int i = 0; i < MAX_VALUE; i++)
        {
            bc.Add(value);

            value++;
            Console.WriteLine("Producing value {0}", value);
            Thread.Sleep(20);
        }
        Thread.Sleep(20);
    }

    static void Consumer()
    {
        foreach(var item in bc.GetConsumingEnumerable())
        {
            Console.WriteLine("An item has been consumed: {0}", item);
        }
    }

    static void Main(string[] args)
    {
        Task t1 =Task.Run(() => producer(valueP));
        Task t2 = Task.Run(() => producer(valueP));
        Task t3 = Task.Run(() => Consumer());

        Task.WaitAll(t1, t2);

        bc.CompleteAdding(); // signal end of producing values

        t3.Wait(); // wait for consumer to read any unread values
    }
}

现在正在单独的线程上使用这些项目,而生产者正在添加项目,因此未达到上限。

另一种选择是,不是使用bc.CompleteAdding();GetConsumingEnumerable(),而是使用GetConsumingEnumerableCancellationToken作为参数来停止根据用户输入使用项目示例

此外,这也可以(替换现有代码):

static void Consumer()
{
    int item;
    while(!bc.IsCompleted)
    {
        item = bc.Take();
        Console.WriteLine("An item has been consumed: {0}", item);
        Thread.Sleep(20);
    }
}

static void Main(string[] args)
{
    Task t1 =Task.Run(() => producer(valueP));
    Task t2 = Task.Run(() => producer(valueP));
    Task t3 = Task.Run(() => Consumer());

    Task.WaitAll(t1, t2);

    bc.CompleteAdding(); // signal end of producing values

    t3.Wait(); // wait for consumer to read any unread values
}

我希望这会给你一个更好的主意。如果您有一个现实世界的问题需要解决,请解释您还需要什么。因为有很多方法可以从阻塞集合中读取,但这取决于您的具体情况。