.NET TPL 声明问题

.NET TPL Declaration issues

我很难理解 TPL,我找不到很多关于它的清晰文章。大多数似乎使用带有 lambda 表达式的简单示例。

我有一个 C# 函数

int[] PlayGames(int[][] boardToSearch, int numGamesToPlay) {…}

我想使用 C# 中的 .NET 4.6 TPL 使其可线程化。我想一次启动最多 8 个这些功能,等到它们全部完成,捕获结果并继续。

我似乎无法获得正确的类型并且它没有按预期工作。

这是我目前得到的:

Task<int[]> PlayGames(int[][] boardToSearch, int numGamesToPlay) {…code that takes a long time…}

private int FindBestMove(int[][] boardToSearch, int numGamesToPlay)
{
    …
    var taskList = new List<Task>();

    taskList.Add(Task.Factory.StartNew(() => { return PlayGames(boardToSearch, numGamesToPlay); }));
    taskList.Add(Task.Factory.StartNew(() => { return PlayGames(boardToSearch, numGamesToPlay); }));   

    // Tests
     Task a = taskList.First();

     var a1 = a.Result; // NOT ALLOWED!? Viewable in debugger, cannot access it.

     var b = Task.FromResult(a); 

     var b1 = b.Result; // Works but cannot access child member Result. Debugger sees it, I can’t!?     

    Task.WaitAll(taskList.ToArray());
    …
}

这是我的问题

  1. 如何删除 lambda 表达式 () => { return PlayGames(boardToSearch, numGamesToPlay); }?我想以某种方式使用 Func(),但我一辈子都不知道怎么说“Task.Factory.StartNew<int[]>(Func(PlayGames(boardToSearch, numGamesToPlay)))”。

  2. 为什么我需要使用StartNew()?当我执行 taskList.Add(PlayGames(int[][] boardToSearch, int numGamesToPlay)) 时,它会同步执行!?以这种方式将任务添加到列表的正确语法是什么?我是否需要声明我传递给 new Task(Func(PlayGames))?

  3. 之类的某种 Func
  4. 当你在执行 Task a = taskList.First() 行后查看变量 a 时,它在调试窗格中清楚地显示了一个名为 Result 的成员。如果我扩展该结果,它包含正确的数据!但是,如果我单击向结果成员添加监视,则会出现错误!如果我这样做 a.Result,编译器会给我同样的错误!??所以调试器说它在那里,但我不能自己查看它!??我可以从对象浏览它但不能直接浏览。我附上了此屏幕截图,以便其他人可以看到。

  5. 在远离 lambda 表达式的情况下,使用 .NET 4.6 执行此操作的最简洁方法是什么。我想查看所有类型和声明。

附件是我的调试器的屏幕截图,因此您可以明白我的意思 .Result

让我们从头开始:

1] Func 它只是一个委托,它是 .net 框架库的一部分。 因此,当您传递 () => { return PlayGames(boardToSearch, numGamesToPlay); } 时,这意味着您只是创建了一个类型为 Func<int[]> 的匿名方法。如果将此 lambda 表达式分配给某个变量,则可以检查此类型。 如果你不想使用 lambda,你可以编写一个通用方法并将其放入任务中:Task.Factory.StartNew(YourMethodWhichReturnsIntArray).

2] 当您调用 StartNew() 方法时,它只会创建一个新的 Task 并开始执行它。而已。 taskList.Add(PlayGames(int[][] boardToSearch, int numGamesToPlay)) - 这只是将 Task 放入 taskList。如果在您的 PlayGames 方法中此 Task 未启动,那么您需要稍后再执行此操作。同步与否- 将Task 添加到列表是同步操作,但执行仍然是异步的。任何语法都可能正确或不正确——这取决于复杂性和实现。除了 Task.Factory.StartNew(),您还可以使用 Task.Run() 方法。它做同样的事情,但以一种更短的方式。并且在传递给 Task.

之前没有必要声明一个 func

3] 我认为这是因为 debugger 有能力等待并行 thread/task 的结果,但 watcher 没有。这就是你得到错误的原因。 所以,我会说不要尝试为并行线程结果添加观察者(只是为了跳过可能的错误)。

4] 在远离 lambda 表达式的情况下,使用 .NET 4.6 执行此操作的最简洁方法是什么。我想查看所有类型和声明。

正如我上面所说,没有必要声明 lambda。您可以创建具有相应定义的方法。但是在这里你将参数传递给这个方法会遇到一些困难(但它们仍然可以解决)。因此,lambda - 是将函数传递给 Task 的最简单方法,因为它可以轻松地从创建这些 lambda 的范围中捕获参数。

What is the cleanest way - 同样,这取决于。我们每个人都有自己最干净的方式来完成 运行 新任务。所以,我认为(见下文):

// your function
int[] PlayGames(int[][] boardToSearch, int numGamesToPlay) {…}

private int YouMethodToRun8Tasks(int[][] boardToSearch, int numGamesToPlay)
{
    ...
    var taskList = new List<Task<int[]>>();

    // create and run 8 tasks
    for(var i = 0; i < 8; i++)
    {
        // it will capture the parameters and use them in all 8 tasks
        taskList.Add(Task.Run<int[]>(() => PlayGames(boardToSearch, numGamesToPlay));
    }
    // do something else
    ...   

    // wait for all tasks
    Task.WaitAll(taskList.ToArray());
    // do something else
}

在某些情况下可能被称为最干净的方式。

希望对你有所帮助