【问题标题】:Processing list with threads带线程的处理列表
【发布时间】:2016-07-13 20:39:41
【问题描述】:

我想处理一个包含 5000 个项目的列表。对于每个项目,该过程可能非常快(1 秒)或需要很长时间(> 1 分钟)。但我想以最快的方式处理此列表。

我不能将这 5000 个项目放在 .NET 线程池中,而且我需要知道这些项目何时全部处理完毕,所以我想有一个特定数量的线程并做:

foreach(var item in items)
{
    // wait for a Thread to be available
    // give the item to process to the Thread
}

但是在 c# 中最简单的方法是什么?我应该使用线程,还是可以使用一些更高级别的类?

【问题讨论】:

  • 使用 Parallel ForEach 怎么样?看看:msdn.microsoft.com/en-us/library/dd460720(v=vs.110).aspx
  • @jcvegan OP 说线程池由于某种原因不适用于他们的情况 - 所以包括 Parllel.ForEach 在内的大多数标准方法似乎都已经过时了...... jcmag,请澄清。
  • @AlexeiLevenkov:他说他不能将 5000 个项目放入线程池中。我认为他的意思是他不能启动 5000 个线程,每个项目一个。
  • @AlexeiLevenkov 无法同时启动 5000 个线程。我需要知道所有项目的处理时间。

标签: c# multithreading


【解决方案1】:

我将从Parallel.ForEach 开始并衡量您的表现。这是一种简单而强大的方法,并且调度对于通用调度器来说做得相当不错。

Parallel.ForEach(items, i => { /* your code here */ });

我无法将这 5000 个项目放入 .NET 线程池中

你也不想。创建线程相对昂贵。上下文切换需要时间。如果您说 8 个内核处理 5000 个线程,那么执行时间的一个有意义的部分将是上下文切换。

【讨论】:

  • 每个项目的过程都意味着发出WebRequests(这就是为什么处理每个项目需要随机时间的原因),我尝试了Parallel.ForEach,但随后出现TaskCanceledException错误(与WebRequests有关)跨度>
  • @jcmag 这是代码中其他地方的错误。您可能正在执行异步方法而不等待结果。重要的提示! Parallel.ForEach 不适用于 async/await。
【解决方案2】:

要进行并行处理,这是要使用的结构

Parallel.ForEach(items, (item) => 
{
       ....
}

如果你不想让线程池过载,你可以使用ParallelOptions

var po = new ParallelOptions
{
     MaxDegreeOfParallelism = 5
}

Parallel.ForEach(items, po,(item) => 
{
           ....
}

【讨论】:

  • 无论如何你都不会超载线程池。运行时将选择在大多数情况下执行良好的线程数。
【解决方案3】:

我同意推荐Parallel.ForEach 的答案。在不知道所有细节(比如循环中发生了什么)的情况下,我不能说 100%。只要循环中的迭代没有做任何相互冲突的事情(比如与其他一些不是线程安全的对象的并发操作),那么它应该没问题。

您在评论中提到它引发了异常。这可能是一个问题,因为如果一次迭代引发异常,那么循环将终止,而您的任务仅部分完成。

为避免这种情况,请在循环的每次迭代中处理异常。例如,

var exceptions = new ConcurrentQueue<Exception>();
Parallel.ForEach(items, i => 
{ 
    try
    {
        //Your code to do whatever
    }
    catch(Exception ex)
    {
        exceptions.Enqueue(ex);
    }
});

通过使用ConcurrentQueue,任何迭代都可以安全地添加自己的异常。完成后,您将获得一个例外列表。现在您可以决定如何处理它们。你可以抛出一个新的异常:

if (exceptions.Count > 0) throw new AggregateException(exceptions);

或者,如果有一些东西可以唯一标识每个 item,您可以这样做(例如)

var exceptions = new ConcurrentDictionary<Guid, Exception>();

然后当抛出异常时,

exceptions.TryAdd(item.Id, ex); //making up the Id property

现在您可以明确知道哪些项目成功了,哪些项目失败了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-11
    • 2012-11-12
    • 2015-08-19
    • 2013-09-10
    • 1970-01-01
    相关资源
    最近更新 更多