我很难理解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());
…
}
以下是我的问题
如何删除lambda表达式() => { return PlayGames(boardToSearch, numGamesToPlay); }
?我想以某种方式使用Func()
,但我不能为我的生活弄清楚如何说“Task.Factory.StartNew<int[]>(Func(PlayGames(boardToSearch, numGamesToPlay)))
”。
为什么我需要使用StartNew()
?当我taskList.Add(PlayGames(int[][] boardToSearch, int numGamesToPlay))
时,它会同步执行!?以这种方式将任务添加到列表的正确语法是什么?我是否需要声明一些我传递给new Task(Func(PlayGames))
之类的Func?
在执行第一行任务a = taskList.First()后查看变量a时,它会在调试窗格中清楚地显示一个名为Result的成员。如果我扩展该结果,它包含正确的数据!但是,如果我点击向结果成员添加监视,我会收到错误!如果我做了。结果,编译器给了我同样的错误!??所以调试器说它在那里,但我不能自己查看它!??我可以从一个对象浏览它,但不能直接浏览它。我附上了这个截图,以便其他人可以看到。
使用.NET 4.6执行此操作的最简洁方法是什么,同时远离lambda表达式。我想查看所有类型和声明。
附件是我的调试器的屏幕截图,因此您可以看到我对.Result
答案 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
}
在某些情况下,可能被命名为最干净的方式。
我希望它会对你有所帮助。