3

我正在创建一个索引器,它正在排队需要处理的项目。索引器会将项目添加到其处理器。例如,它将添加 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 个项目。但它不睡觉,并使用肮脏的while循环。

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

变更日志

  1. 更改为 BlockingCollection
  2. 改回并发队列

我检查过的一些事情:

4

2 回答 2

3

我认为这里最简单的解决方案是使用BlockingCollection(可能使用它的GetConsumingEnumerable())和一个长期运行的Task. 当无事可做时,这会浪费 a Thread,但浪费一个Thread并没有那么糟糕。

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

如果您使用的是 .Net 4.5,另一种选择是使用ActionBlockTPL Dataflow。有了它,您就不会浪费任何线程,也不必自己编写困难的线程安全代码。

于 2013-06-21T11:33:38.697 回答
2

我认为信号量可能适合您。你会在这里找到一个很好的解释

另外我建议使用ConcurrentQueue

于 2013-06-21T07:41:21.383 回答