简体   繁体   English

多个使用者并查询 C# BlockingCollection

[英]Multiple consumers and querying a C# BlockingCollection

I am using a .NET 4.0 BlockingCollection to handle a queue of items that each need to be processed by an operation that can take up to a second to process each item.我正在使用 .NET 4.0 BlockingCollection 来处理一个项目队列,每个项目都需要由一个操作处理,该操作最多需要一秒钟来处理每个项目。 This queue of items can be added to by different threads.这个项目队列可以由不同的线程添加到。

I have a couple of questions regarding this a) allowing multiple consumers to work on this BlockingCollection?我对此有几个问题 a) 允许多个消费者处理此 BlockingCollection? I noticed GetConsumingEnumerable(), which seems to be applicable for single consumer scenarios.我注意到GetConsumingEnumerable(),它似乎适用于单一消费者场景。 The reason for having multiple consumers is that the processing, via a named pipe instance, can process up to three of these items at a time, so I thought I could have three consumers.有多个消费者的原因是处理,通过命名管道实例,一次最多可以处理三个这样的项目,所以我认为我可以有三个消费者。

b) Is there a way of checking to see if an item is on this queue, and if so, getting the caller that checks to see if there is an item to block until the item has been processed? b) 有没有办法检查一个项目是否在这个队列中,如果是,让调用者检查是否有一个项目要阻塞,直到该项目被处理?

EDIT:编辑:

Based on Jon Skeet's answer here's some sample code to illustrate multiple consumers acting on a BlockingCollection populated by a single producer, with consumers using GetConsumingEnumerable() :基于 Jon Skeet 的回答,这里有一些示例代码来说明多个消费者对由单个生产者填充的 BlockingCollection 采取行动,消费者使用GetConsumingEnumerable()

static BlockingCollection<string> coll = new BlockingCollection<string>();

static void Consume()
{
    foreach (var i in coll.GetConsumingEnumerable())
    {
        Console.WriteLine(String.Format("Thread {0} Consuming: {1}",  Thread.CurrentThread.ManagedThreadId, i));
        Thread.Sleep(1000);
    }
}

static void Main(string[] args)
{
    int item = 0;

    Task.Factory.StartNew(() =>
    {
        while (true)
        {
            coll.Add(string.Format("Item {0}", item++));
            Thread.Sleep(500);
        }
    });

    for (int i = 0; i < 2; i++)
    {
        Task.Factory.StartNew(() => Consume());
    }

    while (true) ;
}

The items are processed in an interleaved manner between the two consumers operating on the two different threads, eg在两个不同线程上操作的两个消费者之间以交错的方式处理这些项目,例如

Thread 4 Consuming: Item 0
Thread 5 Consuming: Item 1
Thread 4 Consuming: Item 2
Thread 5 Consuming: Item 3
Thread 4 Consuming: Item 4

Multiple consumers can just call Take or TryTake concurrently - each item will only be consumed by a single consumer.多个消费者可以同时调用TakeTryTake - 每个项目只会被一个消费者消费。

However, I believe GetConsumingEnumerable will also do what you want.但是,我相信GetConsumingEnumerable也会做你想做的。 I believe if each caller calls that, each will get a separate consuming enumerable, which again will make sure that each item is only consumed once.我相信如果每个调用者都调用它,每个调用者都会得到一个单独的消费枚举,这将再次确保每个项目只消费一次。 I'm not sure offhand what happens when the queue becomes empty - I don't know whether MoveNext() then blocks, or returns false.我不确定当队列变空时会发生什么 - 我不知道MoveNext()然后是阻塞还是返回 false。

I didn't really follow your second question though...我并没有真正关注你的第二个问题......

GetConsumingEnumerable is in fact safe to call from multiple consumers simultaneously; GetConsumingEnumerable实际上可以安全地同时从多个消费者调用; the enumerable only completes when the collection is marked as complete.可枚举仅在集合标记为完成时完成。 Each item is only consumed once.每个物品只消耗一次。

GetConsumingEnumerable is essentially equivalent to : GetConsumingEnumerable 本质上等同于

while (!IsCompleted)
{
  if (TryTake(out var item, Timeout.Infinite))
    yield return item;
}

plus a bit of cancellation/cleanup logic.加上一些取消/清理逻辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM