【问题标题】:Threading with Parallel.For Adding Lists使用 Parallel.For 添加列表的线程
【发布时间】:2014-06-11 14:06:11
【问题描述】:

我在使用 Parallel.For 方法时遇到问题。我正在打一个 GET 电话以取回一份清单。然后我想把这个列表添加到主列表中。我已经尝试过 addRange ,它不是线程安全的,并且会在列表中返回错误的数据。我也尝试使用 ConcurrentBag 也没有得到正确的数据。当我说没有得到正确的数据时,我的意思是列表中的某些数据要么重复,要么被覆盖。

这是我的代码(更新):

var thisLock = new Object();
var list = new List<Person>();
Parallel.For(1, 10, x =>
{
    request.Page = x;
    response = Get(request); // call to client
    lock(thisLock)
    {
        list.AddRange(response);
    }
}

除了 addRange 或 ConcurrentBag 之外的任何其他想法

【问题讨论】:

  • 锁定列表。还有其他机制,但这是最简单的一种,因为添加到列表中不太可能是代码中的主要性能瓶颈。
  • 为什么不直接锁定列表?
  • 正如@Rotem 所暗示的,添加到列表时需要锁定列表。 Concurrentbag 应该处理这个问题。没有得到正确的数据是什么意思?
  • 当我锁定主列表时只会打乱一个电话。这意味着主列表的一部分中的数据将被覆盖。
  • @Beastwood 这不是lock 的正确方法,您应该定义一个特定的锁定对象。无论如何,ConcurrentBag&lt;T&gt; 是您尝试做的推荐集合。您的数据“重复”这一事实向我表明,您的服务器正在返回重复数据,而不是代码以某种方式复制它。

标签: .net multithreading thread-safety task-parallel-library parallel.for


【解决方案1】:

我在这里做了一些假设,但您的问题似乎是您的request/response 变量不在Parallel.For 调用范围内。

问题是您进行了(可能)同步Get 调用,该调用更新了response 变量,但假设您有X 个线程都在使用相同的响应,如果在任何给定点更新,即当另一个线程正在运行时将其添加到列表中,那么这很容易导致重复数据。

request 也是如此,您有一个明显的竞争条件,这意味着当一个线程更改 request.Page 而另一个线程刚刚要提取数据时,您实际上是在提取数据跨不同线程的同一页面。

解决方案很简单,在本地创建您的request/response 对象

var list = new ConcurrentBag<T>();
Parallel.For(1, 10, x =>
{
    var request = // create new request;
    request.Page = x;
    var response = Get(request); // call to client
    foreach (var item in response) {
        list.Add(item); // add it main list
    }
}

【讨论】:

  • +1 这很可能,我不敢相信我错过了。
  • ConcurrentBag 没有 AddRange。我应该做一个循环来单独添加每个
  • @James 感谢您的帮助。实际上,出于某种原因,我还必须锁定 ConcurrentBag 才能使其工作。然后它仍然只有大约 75% 的时间有效。
  • @Beastwood 如果您使用的是ConcurrentBag&lt;T&gt;,您绝对不需要锁定...您是什么意思它只在 75% 的时间内有效?您是否按照建议在每个线程中重新创建请求和响应?
  • @James 我想我可能已经修好了。我即将进行很多测试,但我认为我犯了一个愚蠢的错误。我声明了一个新请求,但我在上面声明了一个,我只是说 var newResponse = resonse.正如你所知道的那样,我并不擅长编程(YET),但我可能是错的,但这样做我仍然指向内存中的同一个对象,所以现在我提出了一个全新的请求
【解决方案2】:

这是 PLINQ 的一个很好的候选者。然后您可以使用SelectMany 来展平序列。

var list = ParallelEnumerable.Range(1, 10).SelectMany(x =>
{
    var request = // create new request;
    request.Page = x;
    response = Get(request); // call to client
    return response;
}).ToList();

【讨论】:

  • 感谢您的选择。我试过这种方法,它奏效了,但速度有点慢。
猜你喜欢
  • 1970-01-01
  • 2023-04-04
  • 2017-09-26
  • 1970-01-01
  • 1970-01-01
  • 2012-10-15
  • 2015-08-11
  • 1970-01-01
  • 2012-05-24
相关资源
最近更新 更多