依赖任务图:.Net 中整齐的并行执行?

Oli*_*yre 2 .net c# task-parallel-library

.Net 中是否有一种优雅的方法可以在多线程中执行命令图,其中每个命令只有在其所有父命令都被执行后才能执行?

例如,Visual Studio 在多线程编译解决方案时执行此类计算(每个项目在其所有依赖项都已编译后即可编译)。

另一个例子是,如果您想在不破坏关系约束的情况下逐表克隆(或加载)完整数据库,从顶级父表开始,然后执行子表。(假设至少存在一种表的拓扑排序)。

System.Threading.Tasks.Task 似乎使用“task.ContinueWith()”处理命令链的情况,但它似乎不会威胁命令图的情况。

尝试在任务流程开始时将任务与“Task.WaitAll(dependencies)”一起使用是可行的。但它效率不高,因为它开始启动第一个任务(可能会等待)而不是启动准备好的任务。这是我用来检查的代码:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;

namespace ParallelProcessing
{
    /// <summary>
    /// There is the exemple Dependency graph:
    /// 
    /// MyTypedDataSet (long job)         MyTools                   
    ///  ^                   ^             ^   ^                       
    ///  |- Project 0        |             |   |- MyControls           
    ///  ...                 |             |                 
    ///  |- Project 99       |- MyForm ----|
    ///           
    /// 
    /// 'MyControls' should compile quickly after MyTools. Instead of that, MyControls is flooded by Project 0..99
    /// </summary>
    public class TestTasks
    {
        public static void Trace(string message)
        {
            Console.WriteLine("[" + Task.CurrentId + "] " + message);
            System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString("mm:ss.fff") + " [" + Task.CurrentId + "] " + message);
        }
        private static void compile(string projectName, int duration, params Task[] dependency)
        {
            Task.WaitAll(dependency);
            TestTasks.Trace("Start compiling " + projectName);
            Thread.Sleep(duration);
            TestTasks.Trace("End compiling " + projectName);
        }

        public static void RunTest()
        {
            Task taskMyTypedDataSet = new Task(() => TestTasks.compile("MyTypedDataSet", 10000));
            taskMyTypedDataSet.Start();

            Task taskMyTools = new Task(() => TestTasks.compile("MyTools", 2000));
            taskMyTools.Start();

            List<Task> lotOfOtherProjets = new List<Task>();
            for (int i = 0; i < 100; i++)
            {
                string projectName = "Project" + i;
                Task task = new Task(() => TestTasks.compile(projectName, 100, taskMyTypedDataSet));
                task.Start();
                lotOfOtherProjets.Add(task);
            }
            Task taskMyControls = new Task(() => TestTasks.compile("MyControls", 100, taskMyTools));
            taskMyControls.Start();

            Task taskMyForms = new Task(() => TestTasks.compile("MyForms", 100, taskMyControls, taskMyTypedDataSet));
            taskMyForms.Start();

            Task.WaitAll(taskMyTypedDataSet, taskMyForms);
            Task.WaitAll(lotOfOtherProjets.ToArray());
            TestTasks.Trace("Process done");

            Console.ReadLine();
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

您知道此类问题是否有一个巧妙的解决方案?

svi*_*ick 5

您可以用于TaskFactory.ContinueWhenAll()此用途。

例如:

var task1 = Task.Factory.StartNew(() => { /* some computation */ });
var task2 = Task.Factory.StartNew(() => { /* another computation */ });
var continuationTask = Task.Factory.ContinueWhenAll(
    new[] { task1, task2 }, tasks => { /* dependent computation */ });
Run Code Online (Sandbox Code Playgroud)

在 .Net 4.5 中,Task.WhenAll()做了一些非常类似的事情。