我正在创建一个索引器,它正在排队需要处理的项目。索引器会将项目添加到其处理器。例如,它将添加 100 个项目,然后在 3 分钟内不添加项目,然后再添加 50 个项目。
public class Processer
{
private ConcurrentQueue<Item> items;
public void AddItem(Item item)
{
this.items.Enqueue(item);
}
}
这些项目将随机进入,因此我将创建一个单独的线程来出列和处理这些项目。
最好的选择是什么?
不要使用 Collection,而是使用 ThreadPool:
public void AddItem(Item item) { ThreadPool.QueueUserWorkItem(function, item); }
这将自动创建一个队列,并处理项目,但我控制较少,当找到 20 个项目时,它们几乎会停止我的索引器运行并首先完成这个线程池
使用长时间运行的任务:
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,因为可枚举的对象会在一段时间后干涸,并且需要重新检查是否有新项目。
使用一个短暂的运行任务:
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循环。
您的选择,因为 MSDN 建议使用 TPL,我认为不使用线程,但也许有更好的方法来处理这个问题
变更日志
- 更改为 BlockingCollection
- 改回并发队列
我检查过的一些事情:
- 使用工作线程将项目出队(使用 ThreadPool)
- 假人的线程队列(仅使用线程解决方案,而不是 TPL)
- ThreadPool.QueueUserWorkItem 的意外行为(同上)
- C# -使用多线程代码时,优先使用说明 TPL 的ThreadPool 与 Tasks