我正在使用控制台应用程序作为概念验证,并且需要获取异步返回值。
我发现我需要在主方法中使用 Task.WaitAll()
来避免需要一个异步的 "main()" 方法,因为这是不合法的。
现在我卡在了试图找到允许我在 Main() 中使用泛型或仅返回可转换对象的重载上。
我正在使用控制台应用程序作为概念验证,并且需要获取异步返回值。
我发现我需要在主方法中使用 Task.WaitAll()
来避免需要一个异步的 "main()" 方法,因为这是不合法的。
现在我卡在了试图找到允许我在 Main() 中使用泛型或仅返回可转换对象的重载上。
使用 Task.WaitAll
方法不会返回任何值。它只用于等待多个任务完成,然后从这些任务本身获取返回值。
var task1 = GetAsync(1);
var task2 = GetAsync(2);
Task.WaitAll(task1, task2);
var result1 = task1.Result;
var result2 = task2.Result;
如果你只有一个单一的Task
,只需使用Result
属性。 它将返回您的值并阻塞调用线程(如果任务尚未完成):var task = GetAsync(3);
var result = task.Result;
通常情况下,同步等待(阻塞)异步任务并不是一个好主意(“sync over async”),但对于 POC 来说可能还可以接受。
为了最佳实践,请使用新的异步操作方式。替换为:
Task.WaitAll
使用 await Task.WhenAll
Task.WaitAny
使用 await Task.WhenAny
上述代码可以写成:
var task1 = GetAsync(1);
var task2 = GetAsync(2);
var results = await Task.WhenAll(task1, task2);
var result1 = results[0];
var result2 = results[1];
Task.WaitAll
需要所有任务拥有相同的返回类型。 - tchelidzeawait
只是使你的线程变成了非阻塞的。 - RandomElivar totalResult = results.All(b => b)
。(我写这个是因为任务有点神奇,我想表明结果已经失去了那种神秘的氛围) - LosManosTask.WaitAll
或Task.WhenAll
的行时设置了断点。当我检查任务时,我发现它们已经运行-任务属性Status
为= ran to completion
。事实上,我能够完全删除Task.
行,并仍然赋值变量值,所以这是无用的。Task[] taskArray = {
Task.Factory.StartNew(() => ExecuteConcurrentTasks(1).Result),
Task.Factory.StartNew(() => ExecuteConcurrentTasks(2).Result)
};
Task.WaitAll(taskArray);
var s = ((Task<string>)taskArray[0]).Result;
Task.WaitAll
同时执行两个任务,然后在最后一行显示如何访问第一个任务的返回值。private static async void MyAsyncMethod()
{
Task[] taskArray = {
Task.Factory.StartNew(() => ExecuteConcurrentTasks(1).Result),
Task.Factory.StartNew(() => ExecuteConcurrentTasks(2).Result),
Task.Factory.StartNew(() => ExecuteConcurrentTasks(3).Result),
Task.Factory.StartNew(() => ExecuteConcurrentTasks(4).Result)
};
Task.WaitAll(taskArray);
string s1 = ((Task<string>)taskArray[0]).Result;
string s2 = ((Task<string>)taskArray[1]).Result;
string s3 = ((Task<string>)taskArray[2]).Result;
string s4 = ((Task<string>)taskArray[3]).Result;
}
private static async Task<string> ExecuteConcurrentTasks(int passedInt)
{
string s = "Result: " + passedInt.ToString();
if (passedInt == 4)
{
Console.WriteLine(s);
}
else if (passedInt == 3)
{
Console.WriteLine(s);
}
else if (passedInt == 2)
{
Console.WriteLine(s);
}
else if (passedInt == 1)
{
Console.WriteLine(s);
}
return s;
}
.WaitAll
会阻塞程序中的其他部分,如果你运行的不仅仅是一个非常简单的程序,这将是一个问题。此外,.WhenAll
并不意味着任务直到该行才开始异步执行,它只是意味着该块中的代码除非所有指定的任务都完成运行,否则不会继续执行。Task.Factory.StartNew(() => ExecuteConcurrentTasks(1).Result)
没有任何理由地阻塞了一个 ThreadPool
线程,并且使用了 原始的 Task.Factory.StartNew
方法而没有明确指定 TaskScheduler
。正确的方法是 Task.Run(() => ExecuteConcurrentTasks(1))
,或者直接使用 ExecuteConcurrentTasks(1)
。 - Theodor Zoulias
Task.WaitAll()
的实现是否有所改变,但这个答案对我没有用。Task.WaitAll()
只会阻塞线程,执行永远不会到达下一行。如其他答案所建议,使用await Task.WhenAll()
似乎是未来推荐的方式。 - Adrian HristovSynchronizationContext
死锁。正如这个答案所说,阻塞异步任务不是一个好主意,但如果你决定这样做,这就是获取任务结果的方法。 - i3arnonfor(var i = 0; i < 100; i++) { taskList.Add(client.PostAsync("http://localhost:3000...)); } await Task.WhenAll(taskList); foreach(var task in taskList) { System.Diagnostics.Debug.WriteLine(task.Result); }
在获取task.Result时出错,因为Task上没有Result属性或方法? - Kevin Burton