任务后处理在2个任务完成后立即开始

时间:2015-06-23 00:39:21

标签: c# parallel-processing task

我有一项核心任务,即转发一些核心数据和多个其他子任务来获取额外数据。一旦核心任务和任何子任务准备好,就想对核心数据运行一些更丰富的过程。你知道怎么做吗?

想到这样的事情,但不确定它是做我想做的事情:

// Starting the tasks
var coreDataTask = new Task(...);
var extraDataTask1 = new Task(...);
var extraDataTask2 = new Task(...);

coreDataTask.Start();
extraDataTask1.Start();
extraDataTask2.Start();

// Enriching the results
Task.WaitAll(coreDataTask, extraDataTask1);
EnrichCore(coreDataTask.Results, extraDataTask1.Results);

Task.WaitAll(coreDataTask, extraDataTask2);
EnrichCore(coreDataTask.Results, extraDataTask2.Results);

另外考虑到在同一个核心对象上的丰富度,猜猜我需要把它锁定在哪里?

提前致谢!

2 个答案:

答案 0 :(得分:4)

这是另一个利用Task.WhenAny()来检测任务何时完成的想法。

对于这个最小的例子,我只假设核心数据和额外数据是字符串。但你可以调整你的类型。

另外,我实际上并没有进行任何处理。你必须插入你的处理。

另外,我正在做的一个假设,即不是很清楚,因为你大部分都试图并行化数据的收集,因为那是昂贵的部分,但是丰富的部分实际上非常快。基于该假设,您将注意到任务并行运行以收集核心数据和额外数据。但随着数据变得可用,核心数据会同步丰富,以避免因锁定而使代码复杂化。

如果你复制粘贴下面的代码,你应该可以按原样运行它,看看它是如何工作的。

public static void Main(string[] args)
{
    StartWork().Wait();
}

private async static Task StartWork()
{
    // start core and extra tasks
    Task<string> coreDataTask = Task.Run(() => "core data" /* do something more complicated here */);
    List<Task<string>> extraDataTaskList = new List<Task<string>>();
    for (int i = 0; i < 10; i++)
    {
        int x = i;
        extraDataTaskList.Add(Task.Run(() => "extra data " + x /* do something more complicated here */));
    }

    // wait for core data to be ready first.
    StringBuilder coreData = new StringBuilder(await coreDataTask);

    // enrich core as the extra data tasks complete.
    while (extraDataTaskList.Count != 0)
    {
        Task<string> completedExtraDataTask = await Task.WhenAny(extraDataTaskList);
        extraDataTaskList.Remove(completedExtraDataTask);
        EnrichCore(coreData, await completedExtraDataTask);
    }

    Console.WriteLine(coreData.ToString());
}

private static void EnrichCore(StringBuilder coreData, string extraData)
{
    coreData.Append(" enriched with ").Append(extraData);
}

编辑:.NET 4.0版

以下是我将如何为.NET 4.0更改它,同时仍保留相同的整体设计:

  • Task.Run()变为Task.Factory.StartNew()
  • 我没有对任务执行await,而是调用Result,这是一个等待任务完成的阻止调用。
  • 使用Task.WaitAny代替Task.WhenAny,这也是阻止来电。

设计仍然非常相似。两个版本的代码之间的一个重要区别是,在.NET 4.5版本中,只要有await,当前线程就可以自由地执行其他工作。在.NET 4.0版本中,无论何时调用Task.ResultTask.WaitAny,当前线程都会阻塞,直到任务完成。这种差异可能对你来说并不重要。但如果是,只需确保在后台线程或任务中包装并运行整个代码块以释放主线程。

另一个区别在于异常处理。对于.NET 4.5版本,如果任何任务因未处理的异常而失败,则异常将以非常透明的方式自动解包和传播。使用.NET 4.0版本,您将获得AggregateException s,您必须自行解包和处理。如果这是一个问题,请确保事先进行测试,以便知道会发生什么。

就个人而言,我尽量避免Task.ContinueWith。它往往会使代码变得非常丑陋且难以阅读。

public static void Main(string[] args)
{
    // start core and extra tasks
    Task<string> coreDataTask = Task.Factory.StartNew(() => "core data" /* do something more complicated here */);
    List<Task<string>> extraDataTaskList = new List<Task<string>>();
    for (int i = 0; i < 10; i++)
    {
        int x = i;
        extraDataTaskList.Add(Task.Factory.StartNew(() => "extra data " + x /* do something more complicated here */));
    }

    // wait for core data to be ready first.
    StringBuilder coreData = new StringBuilder(coreDataTask.Result);

    // enrich core as the extra data tasks complete.
    while (extraDataTaskList.Count != 0)
    {
        int indexOfCompletedTask = Task.WaitAny(extraDataTaskList.ToArray());
        Task<string> completedExtraDataTask = extraDataTaskList[indexOfCompletedTask];
        extraDataTaskList.Remove(completedExtraDataTask);
        EnrichCore(coreData, completedExtraDataTask.Result);
    }

    Console.WriteLine(coreData.ToString());
}

private static void EnrichCore(StringBuilder coreData, string extraData)
{
    coreData.Append(" enriched with ").Append(extraData);
}

答案 1 :(得分:0)

我认为你可能想要的是“ContinueWith”(文档在这里:https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx)。只要您的丰富不需要按特定顺序完成即可。

代码如下所示:

var coreTask = new Task<object>(() => { return null; });
var enrichTask1 = new Task<object>(() => { return null; });
var enrichTask2 = new Task<object>(() => { return null; });

coreTask.Start();
coreTask.Wait();

//Create your continue tasks here with the data you want. 
enrichTask1.ContinueWith(task => {/*Do enriching here with task.Result*/});

//Start all enricher tasks here. 
enrichTask1.Start();

//Wait for all the tasks to complete here. 
Task.WaitAll(enrichTask1);

您仍然需要首先运行CoreTask,因为在完成所有丰富任务之前需要完成。但是从那里你可以开始所有任务,告诉他们什么时候完成“ContinueWith”做其他事情。

您还应该快速浏览一下“Enricher Pattern”,它可以帮助您实现您想要实现的目标(在线程之外)。例如:http://www.enterpriseintegrationpatterns.com/DataEnricher.html