Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过Parallel.ForEach保留单个后台线程_C#_Multithreading - Fatal编程技术网

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请求,这些请求可以触发长时间运行的进程,从而使进程在单个线程上运行。工作流程如下:

  • 用户发送一个HTTP POST(来自我们正在开发的Angular站点),其中包含执行该POST所需的所有信息,包括要执行的路由

  • 帖子点击了一个WebAPI
    ApiController
    ,它本身调用一个服务

    • 服务本身是通过Unity与
      ContainerControlled LifetimeManager
      实例化的,因此它一直在后台运行。(我们已经测试过这种情况。)
  • 该服务通过EntityFramework将来自POST的数据添加到数据库表中

    • 如果服务已经在处理项目,那么它只需在此停止
    • 如果服务未处理项目,则会开始处理
  • 该服务通过从数据库中一次检索一个项目来处理项目。处理每个项目的方法是获取其所有数据并向另一个服务发送HTTP POST(这会启动一个无法同时运行的进程,这是我们使用的库的限制),然后等待它完成。一旦完成,它会将该项的状态设置为Success/Error,然后从数据库中获取下一项并重复,直到数据库中没有其他要处理的项为止

  • 根据优先级以及之前是否运行过,从数据库中选择项目(即,状态为InQueue,而不是Success/Error)

  • 我们的目标有三个好处:

  • 使用DB作为后备存储,我们可以在应用程序因某种原因死亡的情况下获得一些安全性

  • 当队列没有要处理的项时,不需要继续轮询数据库。它只是无所事事地坐在那里,直到一个新的项目排队,在这种情况下,整个过程再次启动

  • 由于内部没有备份收集,当项目从数据库中取出并且应用程序由于某种原因死亡时,我们不需要担心数据丢失。这与#1有关

  • 我们面临的最大危险——我遇到的问题是——这里的最终入口点是一个网站,以及该网站上的一个按钮。因此,完全有可能100人同时按下按钮,最终,这场混乱结束的过程必须以串行方式运行。因此,我们需要将所有这些请求集中到一个文件行中。因此,整个队列应由一个线程处理。这里我使用一个名为
    \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();
                }
            }
        }
    }