C# 通过Parallel.ForEach保留单个后台线程
(好吧,我把这件事搞得一团糟,所以我要把它清理干净,并在这里提供我能提供的所有信息。请看这篇文章的底部,以更好地解释我要做的事情。我把其余的留在这里,希望有人能看到我弄得一团糟,并从我的错误中学习如何写一篇更好的文章。:) 请参阅题为“更好的解释”的部分,好吧,更好的解释C# 通过Parallel.ForEach保留单个后台线程,c#,multithreading,C#,Multithreading,(好吧,我把这件事搞得一团糟,所以我要把它清理干净,并在这里提供我能提供的所有信息。请看这篇文章的底部,以更好地解释我要做的事情。我把其余的留在这里,希望有人能看到我弄得一团糟,并从我的错误中学习如何写一篇更好的文章。:) 请参阅题为“更好的解释”的部分,好吧,更好的解释 编辑2:我为不清楚而道歉ItemStore不是一个集合。在本例中,它是一个DB支持的服务。我已经更新了我的代码 编辑:其他信息 备份存储将是一个DB。这意味着,我们可以持久化队列中的项目,而不必担心在应用程序死亡时丢失项
编辑2:我为不清楚而道歉
ItemStore
不是一个集合。在本例中,它是一个DB支持的服务。我已经更新了我的代码
编辑:其他信息
- 备份存储将是一个DB。这意味着,我们可以持久化队列中的项目,而不必担心在应用程序死亡时丢失项目。这也意味着从数据库中添加/检索项可能会很慢。(即,速度不如内存中的快)
- 正因为如此,这也意味着我们不想一直在内存中保存一个集合。首选方法是返回到DB以获取下一项,同样是为了持久性安全
- 最终,项目来自web服务调用。本质上,排队将是一个WebAPI路由,浏览器将对其执行HTTP POST
- 最后,我们试图解决的中心问题是将大量请求潜在地汇集到一个FIFO队列中,这主要是由于我们使用的第三方库的限制。因此,我们的目标是,比如说,同时获得10个请求,并逐一处理它们
我正在尝试创建一个简单的处理队列。在
Enqueue
上,它向存储添加一个项目,然后检查处理线程是否已经在运行。如果是的话,就完成了;如果不是,它将启动运行队列的线程
队列线程本身查询存储中的下一项并对其进行处理。然后,它查询商店中的下一个项目,并一直运行,直到项目用完。然后它停止处理并关闭,直到下一个项目进入队列
代码基本上如下所示:
// Service that saves and retrieves items from a database
private IItemService _service;
// Item processor
private IItemProcessor _itemProcessor;
public void Enqueue(object item)
{
_service.Save(item);
if (_isRunning)
{
// If the queue is processing, the item just gets added to the DB and
// the processing function will pull it off of the DB when needed.
return;
}
// If it's not already running, process whatever queue items are in the DB.
ProcessQueue();
}
private void ProcessQueue()
{
_workerThread = new ThreadStart(ProcessQueueInternal);
_workerThread.Start();
}
private void ProcessQueueInternal()
{
// _service.GetNextItem() retrieves an item from the DB based on several
// factors, including whether another instance of a queue has claimed it,
// priority, etc.
object item;
while (item = _service.GetNextItem()) != null)
{
_itemProcessor.ProcessItem(item);
}
// No more items in the DB, so the queue should sit idle until a new
// item is enqueued.
_isRunning = false;
}
我正在使用一个Parallel.ForEach()
循环测试这个队列,如下所示:
Parallel.ForEach(myItems, item => Enqueue(item));
我遇到的问题是,队列偶尔会触发两次,这是我想要避免的。不是经常发生,但这种情况已经发生得够多了,我想防止这种情况发生
我怎样才能避开这件事?多个项目可能会同时排队,我需要确保一次只运行一个后台线程。最好的方法是简单的,像这样吗
private void ProcessQueue()
{
if (_workerThread == null || (_workerThread != null && _workerThread.IsAlive))
{
_workerThread = new ThreadStart(ProcessQueueInternal);
_workerThread.Start();
}
}
还是有更好的办法?简单是这里的目标,仅次于有效性
更好的解释 目标:收集一组HTTP请求,这些请求可以触发长时间运行的进程,从而使进程在单个线程上运行。工作流程如下:
ApiController
,它本身调用一个服务
- 服务本身是通过Unity与
实例化的,因此它一直在后台运行。(我们已经测试过这种情况。)ContainerControlled LifetimeManager
- 如果服务已经在处理项目,那么它只需在此停止
- 如果服务未处理项目,则会开始处理
\u workerThread
的线程。我面临的问题是确保\u workerThread
在任何周期都实例化并启动一次。即:
- 正在处理队列,新项目进入:不要启动新线程
- 队列未被处理,新项目进入:启动新线程
_context.WebRequestQueueItems.Add(someItemEntity);
_context.SaveChanges();
_context.WebRequestQueueItems.AddOrUpdate(someItemEntity);
_context.SaveChanges();
_context.WebRequestQueueItems.Remove(someItemEntity);
_context.SaveChanges();
return _context
.WebRequestQueueItems
.OrderByDescending(item => item.Priority)
.FirstOrDefault();
private static object _gate = new object();
private void ProcessQueue()
{
if (_workerThread == null || (_workerThread != null && _workerThread.IsAlive))
{
lock (_gate)
{
if (_workerThread == null || (_workerThread != null && _workerThread.IsAlive))
{
_workerThread = new ThreadStart(ProcessQueueInternal);
_workerThread.Start();
}
}
}
}