我正在创建一个索引器,它是Enqueueing需要处理的项目。索引器将向其处理器添加项目。例如,它将添加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个项目。但它不会睡觉,并使用脏循环。
您的选择,因为MSDN建议使用TPL,我认为不使用Threads,但也许有更好的方法来处理这个问题
答案 0 :(得分:2)
我认为信号量对你来说可能是正确的。你找到了很好的表达方式here
另外我建议使用ConcurrentQueue
答案 1 :(得分:2)
我认为这里最简单的解决方案是使用BlockingCollection
(可能使用其GetConsumingEnumerable()
)以及长期运行的Task
。如果无事可做,这将浪费Thread
,但浪费的Thread
并没有那么糟糕。
如果你不能浪费Thread
,那么你可以选择类似#3的东西。但是你必须非常小心地使它成为线程安全的。例如,在您的代码中,如果Task
未运行并且同时从两个线程调用AddItem()
,则最终会创建两个Task
,这几乎可以肯定错。
另一个选择,如果您使用.Net 4.5,则使用来自TPL Dataflow的ActionBlock
。有了它,你就不会浪费任何线程而且你不必自己编写困难的线程安全代码。