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

本文关键字:任务 开始 2个 后处理 | 更新日期: 2023-09-27 17:59:17

我有一个核心任务为我检索一些核心数据,还有多个子任务为我提取额外的数据。希望在核心任务和任何子任务准备就绪后立即对核心数据运行一些浓缩过程。你知道怎么做吗?

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

// 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个任务后立即开始

这里有另一个利用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,当前线程都会阻塞,直到Task完成。这种差异可能对你来说并不重要。但如果是这样,只需确保在后台线程或任务中包装并运行整个代码块,即可释放主线程。

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

就我个人而言,我尽量避免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);
}

我想您可能想要的是"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,因为这是在所有丰富任务之前完成的要求。但从那里你可以开始所有的任务,并告诉他们什么时候完成,"继续"做其他事情。

您还应该快速查看"Enricher Pattern",它可能会帮助您实现您想要实现的目标(线程之外)。示例如下:http://www.enterpriseintegrationpatterns.com/DataEnricher.html