并行任务按顺序处理

时间:2011-03-04 15:10:09

标签: c# parallel-processing task task-parallel-library

我正试图围绕整个并行编程概念,主要关注任务,所以我一直在尝试这种情况,例如,最多9个并行任务将在一段随机的时间内完成他们的工作:

/// <remarks>
/// A big thank you to the awesome community of StackOverflow for 
/// their advice and guidance with this sample project
/// http://stackoverflow.com/questions/5195486/
/// </remarks>

RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
byte[] buffer = new byte[4];
random.GetBytes(buffer);

// Creates a random number of tasks (not less than 2 -- up to 9)
int iteration = new Random(BitConverter.ToInt32(buffer, 0)).Next(2,9);
Console.WriteLine("Creating " + iteration + " Parallel Tasks . . .");
Console.Write(Environment.NewLine);

Dictionary<int, string> items = new Dictionary<int, string>();

for (int i = 1; i < iteration + 1; i++) // cosmetic +1 to avoid "Task N° 0"
{
    items.Add(i, "Task # " + i);
}

List<Task> tasks = new List<Task>();

// I guess we should use a Parallel.Foreach() here
foreach (var item in items)
{
    // Creates a random interval to pause the thread at (up to 9 secs)
    random.GetBytes(buffer);
    int interval = new Random(BitConverter.ToInt32(buffer, 0)).Next(1000, 9000);

    var temp = item;
    var task = Task.Factory.StartNew(state =>
    {
        Console.WriteLine(String.Format(temp.Value + " will be completed in {0} miliseconds . . .", interval));
        Thread.Sleep(interval);

        return "The quick brown fox jumps over the lazy dog.";

    }, temp.Value).ContinueWith(t => Console.WriteLine(String.Format("{0} returned: {1}", t.AsyncState, t.Result)));

    tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());

但不幸的是,它们是按顺序处理而不是并行处理。

如果你们能帮助我,我真的很高兴 - 也许我应该使用Parallel.ForEach而不是常规的?

再次提出任何建议都会非常感激。

修改 更新了代码示例两次以反映评论者的贡献。

3 个答案:

答案 0 :(得分:4)

您正在循环中的每个任务上调用task.Result ...这意味着它将在创建下一个任务之前等待一个任务的结果。试试这个:

// Create all the tasks
foreach (var item in items)
{
    // ... Create and start tasks as before
    tasks.Add(task);
}

// Now wait for them all, printing the results
foreach (var task in tasks)
{
    Console.WriteLine(task.AsyncState + " returned: " + task.Result);
}

现在这将阻止立即创建的第一个任务 - 所以即使(比方说)第5个任务更早完成,在第1个完成之前你不会看到任何结果......但它们将并行执行。

接下来你要改变你的循环:

foreach (var item in items)
{
    var itemCopy = item;
    // Use itemCopy in here instead of item
}

否则你正在捕获循环变量,这被认为是有害的(part 1; part 2)。

但是,您可能 应该使用Parallel.ForEach。值得理解为什么它之前失败了。

答案 1 :(得分:1)

调用Task.Result块。由于您在foreach项目中执行此操作,因此最终创建一项任务,然后等待其完成,然后再转到下一项目。

尝试将呼叫转移到Task.Result

之外的foreach
List<Task<string>> tasks = new List<Task<string>>();


foreach (var item in items)
{
    random.GetBytes(buffer);
    int interval = new Random(BitConverter.ToInt32(buffer, 0)).Next(1000, 9000);

    var task = Task.Factory.StartNew(state =>
    {
        Console.WriteLine(String.Format(item.Value + " will be completed in {0} miliseconds . . .", interval));
        Thread.Sleep(interval);

        return "The quick brown fox jumps over the lazy dog.";

    }, item.Value);

    tasks.Add(task);
}

foreach (var task in tasks)
{
    Console.WriteLine(task.AsyncState + " returned: " + task.Result);
} 

修改

正如评论中所要求的那样。这是一个版本,可以在每个任务完成后使用ConinueWith打印结果。任务列表现在也可以再次为List<Task>。最后仍需要进行WaitAll调用,以确保方法在每个任务完成之前不会返回,但每个任务都会在完成后打印出结果。

RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
byte[] buffer = new byte[4];
random.GetBytes(buffer);

// Creates a random number of tasks (not less than 2 -- up to 9)
int iteration = new Random(BitConverter.ToInt32(buffer, 0)).Next(2, 9);
Console.WriteLine("Creating " + iteration + " Parallel Tasks . . .");

Dictionary<int, string> items = new Dictionary<int, string>();

for (int i = 1; i < iteration + 1; i++) // cosmetic +1 to avoid "Task N° 0"
{
    items.Add(i, "Parallel Task N° " + i);
}

List<Task> tasks = new List<Task>();

// I guess we should use a Parallel.Foreach() here
foreach (var item in items)
{
    // Creates a random interval to pause the thread at (up to 9 secs)
    random.GetBytes(buffer);
    int interval = new Random(BitConverter.ToInt32(buffer, 0)).Next(1000, 9000);

    // http://stackoverflow.com/questions/5195486/
    var temp = item;
    var task = Task.Factory.StartNew(state =>
    {
        Console.WriteLine(String.Format(temp.Value + " will be completed in {0} miliseconds . . .", interval));
        Thread.Sleep(interval);

        return "The quick brown fox jumps over the lazy dog.";

    }, temp.Value).ContinueWith(t => Console.WriteLine(t.AsyncState + " returned: " + t.Result));
    tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());

答案 2 :(得分:1)

由于您在每个任务上调用task.Result,因此您正在创建一个阻止调用,导致循环等待该任务返回。

您应首先创建所有任务,然后在单独的循环中读取结果。