多线程中的队列和出队

时间:2013-06-21 07:08:39

标签: c# multithreading task-parallel-library task threadpool

我正在创建一个索引器,它是Enqueueing需要处理的项目。索引器将向其处理器添加项目。例如,它将添加100个项目,然后不会添加3分钟的项目并添加另外50个项目。

public class Processer
{
    private ConcurrentQueue<Item> items;

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

这些项目将以随机的间隔进入,因此我将创建一个单独的线程来出列并处理这些项目。

最佳选择是什么?

  1. 不要使用Collection,而是使用ThreadPool:

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

    这将自动创建一个队列,并处理这些项目,但我控制较少,当找到20个项目时,它们几乎会阻止我的索引器运行并首先完成此线程池

  2. 使用长时间运行的任务:

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

    但我讨厌while()和thread.sleep我必须使用,因为可枚举会在一段时间后干涸,如果有新项目则需要重新检查。

  3. 使用短期任务:

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

    这可能更好吗?但是启动一个线程是一个“昂贵”的操作,我不知道我是否可以错过项目,因为我检查IsCompleted,这可能是在结束while循环的过程中,这样就缺少了1个项目。但它不会睡觉,并使用脏循环。

  4. 您的选择,因为MSDN建议使用TPL,我认为不使用Threads,但也许有更好的方法来处理这个问题

  5. 更新日志

    1. 已更改为BlockingCollection
    2. 更改回ConcurrentQueue
    3. 我检查了一些事情:

2 个答案:

答案 0 :(得分:2)

我认为信号量对你来说可能是正确的。你找到了很好的表达方式here

另外我建议使用ConcurrentQueue

答案 1 :(得分:2)

我认为这里最简单的解决方案是使用BlockingCollection(可能使用其GetConsumingEnumerable())以及长期运行的Task。如果无事可做,这将浪费Thread,但浪费的Thread并没有那么糟糕。

如果你不能浪费Thread,那么你可以选择类似#3的东西。但是你必须非常小心地使它成为线程安全的。例如,在您的代码中,如果Task未运行并且同时从两个线程调用AddItem(),则最终会创建两个Task,这几乎可以肯定错。

另一个选择,如果您使用.Net 4.5,则使用来自TPL Dataflow的ActionBlock。有了它,你就不会浪费任何线程而且你不必自己编写困难的线程安全代码。