使用Parallel.For添加列表进行线程处理

时间:2014-06-11 14:06:11

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

我在使用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之外的任何其他想法

2 个答案:

答案 0 :(得分:4)

我在这里做了一些假设,但您的问题似乎是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 :(得分:0)

这是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();