.NET TPL声明问题

时间:2016-05-01 23:17:50

标签: c# task-parallel-library .net-4.6

我很难理解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))之类的Func?

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

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

  5. 附件是我的调试器的屏幕截图,因此您可以看到我对.Result

    的含义

    enter image description here

1 个答案:

答案 0 :(得分:2)

让我们从顶部开始:

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能够等待并行线程/任务的结果,但watcher没有。这就是你得到错误的原因。 所以,我想说不要尝试为并行线程结果添加观察器(只是为了跳过可能的错误)。

4]使用.NET 4.6执行此操作的最简洁方法是什么,同时远离lambda表达式。我希望看到所有类型和声明。

正如我上面所说,没有必要声明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
}
在某些情况下,

可能被命名为最干净的方式。

我希望它会对你有所帮助。