几个任务操纵同一个对象

时间:2014-01-23 08:47:51

标签: c# task-parallel-library

所以我只是在c#中使用Task类进行一些实验,并发生以下事情。

这是我调用的方法

static async Task<List<int>> GenerateList(long size, int numOfTasks)
{
    var nums = new List<int>();

    Task[] tasks = new Task[numOfTasks];

    for (int i = 0; i < numOfTasks; i++)
    {
        tasks[i] = Task.Run(() => nums.Add(Rand.Nex())); // Rand is a ThreadLocal<Random>
    }

    for (long i = 0; i < size; i += numOfTasks)
    {
        await Task.WhenAll(tasks);
    }

    return nums;
}

我这样称呼这个方法

var nums = GenerateList(100000000, 10).Result;

之前我使用任务生成需要4-5秒。在我实现这样的方法后,如果我传递10-20个任务数,生成时间降低到1.8-2.2秒,但是由方法返回的List具有{numOfTask 1}}其中的元素数量,因此在这种情况下返回10个数字的List。可能是我写错了。这可能是什么问题。或者可能有另一种解决方案。所有我想要在同一个列表中添加数字的任务很多,因此生成时间至少要快两倍。在此先感谢

3 个答案:

答案 0 :(得分:2)

WhenAll不执行任务;它只是(异步)等待它们完成。您的代码只创建了10个任务,这就是为什么您只获得10个数字。另外,正如@Mauro指出的那样,List<T>.Add不是线程安全的。

如果要进行并行计算,请使用Parallel或并行LINQ,而不是async

static List<int> GenerateList(int size, int numOfTasks)
{
    return Enumerable.Range(0, size)
        .AsParallel()
        .WithDegreeOfParallelism(numOfTasks)
        .Select(_ => Rand.Value.Next())
        .ToList();
}

答案 1 :(得分:1)

正如斯蒂芬所解释的那样,你只创造了10个任务。

另外,我相信通用列表上的Add操作不是线程安全的。您应该使用锁定机制,或者,如果您要定位框架4或更新,请使用thread-safe collections

答案 2 :(得分:-1)

您要添加到以下循环中的列表中,该循环仅运行10次

for (int i = 0; i < numOfTasks; i++)
    {
        tasks[i] = Task.Run(() => nums.Add(Rand.Nex())); // Rand is a ThreadLocal<Random>
    }

你可以改为

for (int i = 0; i < numOfTasks; i++)
    {
        tasks[i] = new Task(() => nums.Add(Rand.Nex()));
    }