我有一个异步调用的层次结构。看起来基本上是这样的:
async MyObject MainWorker()
{
return await Task.Run(SomeSynchronousFunction);
}
async MyObject InbetweenFunction1()
{
//do some things
return await MainWorker();
}
async MyObject InbetweenFunction2()
{
//do some things
return await MainWorker();
}
void ReturnObject TopFunction()
{
Task<MyObject> return1 = InbetweenFunction1();
Task<MyObject> return2 = InbetweenFunction2();
while (!return1.IsComplete || !return2.IsComplete)
Thread.Sleep(100);
//access return1.Return and return2.Return values and return from this function
}
所以我有一些级别的异步调用。顶级方法进行两次异步调用,等待它们完成(通过轮询),然后访问它们的返回值并对值进行处理。问题是,没有任何异步调用完成。与Task.Run异步调用的函数SomeSynchronousFunction应该花费几秒钟的时间,但我等待30秒以上没有结果。
这是我第一次尝试使用新的async / await关键字。我做了一件明显不对的事吗?
答案 0 :(得分:4)
我做错了什么?
是的,你正忙着等待(循环直到条件变为true
),除非没有其他选择,否则你不应该这样做。此处有一个更好的选择:使用Wait()
或Task.WaitAll()
。
但这样做很可能实际上不会使你的代码工作。真正的问题可能是the classic await
deadlock:你处于同步上下文(通常是UI线程或ASP.NET请求上下文),而你的await
尝试在该上下文中恢复,但是你'还要阻止等待Task
s完成的上下文。
解决此问题的正确方法是在await
中使用TopLevelFunction()
。这将要求您同时创建该函数async
,这意味着它的调用者现在也必须是async
,等等。这称为“async
一直”。
所有async
方法都应该是async Task
方法,但顶级事件处理程序除外,它们必须是async void
。 (但是你不应该在其他任何地方使用async void
方法,因为它们不能被await
编辑。)
这意味着您的功能将变为:
async Task<ReturnObject> TopFunction()
{
Task<MyObject> return1 = InbetweenFunction1();
Task<MyObject> return2 = InbetweenFunction2();
await Task.WhenAll(return1, return2);
//access await return1 and await return2 values and return from this function
}
答案 1 :(得分:1)
以下是一些可行的代码:
private async Task<string> DoMainWork()
{
await Task.Delay(3000);
return "MainWorker";
}
private async Task<string> InbetweenFunction1()
{
// do something
await Task.Delay(1000);
return await DoMainWork() + "1";
}
private async Task<string> InbetweenFunction2()
{
// do something
await Task.Delay(2000);
return await DoMainWork() + "2";
}
public string TopFunction()
{
string return1 = null;
string return2 = null;
var taskList = new List<Task>();
taskList.Add(Task.Run(async () => return1 = await InbetweenFunction1()));
taskList.Add(Task.Run(async () => return2 = await InbetweenFunction2()));
Task.WaitAll(taskList.ToArray());
return return1 + return2;
}
答案 2 :(得分:0)
你想要的是一个额外的功能来组合以前的任务
public async Task<string> Combine(Task<string> a, Task<string b){
var aa = await a;
var bb = await b;
return aa + bb;
}
和
public string TopFunction()
{
var task = Combine(InBetweenFunction2(), InVetweenFunction2());
// Blocking call
// Really you shouldn't be doing this unless you really really
// have to.
task.Wait();
return task.Result;
}
请注意,really really
必须使用阻止调用的其中一个位置位于应用程序的入口点main function
,该入口点无法声明为async
,因此可以&#39 ; t使用await