简体   繁体   English

多线程中的排队和出队

[英]Queue and Dequeue in multiple threads

I'm creating an indexer which is Enqueueing items which needs to be processed.我正在创建一个索引器,它将需要处理的项目排入队列。 The indexer will add items to its processor.索引器将向其处理器添加项目。 It will for example add 100 items, then doesn't add items for 3 minutes and add another 50 items.例如,它将添加 100 个项目,然后在 3 分钟内不添加项目并添加另外 50 个项目。

public class Processer
{
    private ConcurrentQueue<Item> items;

    public void AddItem(Item item)
    {
        this.items.Enqueue(item);
    }
}

The items will come in at random intervals, so I will create a separate thread to dequeue and process these items.这些项目将随机出现,因此我将创建一个单独的线程来出列和处理这些项目。

What would be the best option to use?什么是最好的选择?

  1. Don't use a Collection, but use the ThreadPool:不要使用集合,而是使用线程池:

     public void AddItem(Item item) { ThreadPool.QueueUserWorkItem(function, item); }

    This will automatically create a Queue, and process the items, but I have less control, when 20 items are found, they will almost stop my indexer to run and first finish this thread pool这将自动创建一个队列,并处理项目,但我的控制较少,当找到 20 个项目时,它们几乎会停止我的索引器运行并首先完成此线程池

  2. Use a long running Task:使用长时间运行的任务:

     public Processer() { this.task = Task.Factory.StartNew(() => DequeueItems(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); } public DequeueItems() { while(true) { Item item = null; while(this.items.TryDequeue(out item) { this.store.ExecuteIndex((AbstractIndexCreationTask)item); } Thread.Sleep(100); } }

    But I hate the while() and thread.sleep I've got to use, since the enumerable will dry up after some time, and it will need recheck if there are new items.但是我讨厌我必须使用的 while() 和 thread.sleep,因为可枚举会在一段时间后枯竭,并且需要重新检查是否有新项目。

  3. Use a short running task:使用一个短期运行的任务:

     public Processer() { } private void Run() { this.task = Task.Factory.StartNew(() => DequeueItems(), CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default); } public void AddItem(Item item) { this.items.Add(item); if(this.task == null || this.task.isCompleted) this.Run(); } public DequeueItems() { Item item = null; while(this.items.TryDequeue(out item) { this.store.ExecuteIndex((AbstractIndexCreationTask)item); } }

    This might be nicer?这可能更好? But starting a thread is a "expensive" operation, and I don't know if I can miss items since I check IsCompleted, which can be in the process of ending the while loop and this way missing 1 item.但是启动一个线程是一个“昂贵”的操作,我不知道我是否会因为我检查 IsCompleted 而错过项目,它可能正在结束 while 循环的过程中,这样就错过了 1 个项目。 But it doesn't sleep, and use a dirty while loop.但它不会休眠,并使用脏的 while 循环。

  4. Your option, since MSDN recommends to use the TPL, I thought not to use Threads, but maybe there are better ways dealing with this problem您的选择,因为 MSDN 建议使用 TPL,我认为不要使用线程,但也许有更好的方法来处理这个问题

Changelog变更日志

  1. Changed to BlockingCollection改为 BlockingCollection
  2. Changed back to ConcurrentQueue改回 ConcurrentQueue

Some things I've checked:我检查过的一些事情:

I think a Semaphore might be the right thing for you.我认为信号量可能适合你。 You'll find a pretty good explanation of it here你会在这里找到一个很好的解释

In Addition I would suggest to use a ConcurrentQueue此外,我建议使用ConcurrentQueue

I think the simplest solution here is to use BlockingCollection (probably using its GetConsumingEnumerable() ) along with a long-running Task .我认为这里最简单的解决方案是使用BlockingCollection (可能使用它的GetConsumingEnumerable() )以及长时间运行的Task When there's nothing to do, this will waste a Thread , but a single wasted Thread is not that bad.当无事可做时,这会浪费一个Thread ,但一个浪费的Thread并没有那么糟糕。

If you can't afford to waste that Thread , then you can go with something like your #3.如果您不能浪费那个Thread ,那么您可以使用类似 #3 的东西。 But you have to be very careful about making it thread-safe.但是你必须非常小心使它成为线程安全的。 For example, in your code, if the Task isn't running and AddItem() is called from two threads at the same time, you end up creating two Task s, which is almost certainly wrong.例如,在您的代码中,如果Task没有运行并且同时从两个线程调用AddItem() ,您最终会创建两个Task ,这几乎肯定是错误的。

Another option, if you're on .Net 4.5 is to use ActionBlock from TPL Dataflow.如果您使用的是 .Net 4.5,另一种选择是使用来自 TPL Dataflow 的ActionBlock With it, you're not wasting any threads and you don't have to write the difficult thread-safe code yourself.有了它,您就不会浪费任何线程,也不必自己编写困难的线程安全代码。

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

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