Reputation: 1326
I have a hierarchy of async calls I'm making. Looks basically like this:
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
}
So I have a few levels of async calls. The top level method makes two async calls, waits for them to complete (via polling) and then accesses their return values and does something with the values. The problem is, none of the async calls ever finish. The function SomeSynchronousFunction which is called asynchronously with Task.Run should take a few seconds tops, but I wait 30+ seconds and no results.
This is my first attempt at using the new async/await keywords. Am I doing something obviously wrong?
Upvotes: 0
Views: 536
Reputation: 245046
Am I doing something obviously wrong?
Yes, you're doing busy waiting (looping until a condition becomes true
), which you shouldn't do unless there is no other option. And there is a better option here: use Wait()
, or Task.WaitAll()
.
But doing that most likely wouldn't actually make your code work. The real problem is probably the classic await
deadlock: you're in a synchronization context (commonly either the UI thread or the ASP.NET request context) and your await
s try to resume on that context, but you're also blocking that context waiting on the Task
s to finish.
The right way to solve this would be to use await
in TopLevelFunction()
too. This would require you to make that function async
too, which then means its caller now has to be async
also, etc. This is called “async
all the way”.
All the async
methods should be async Task
methods, with the exception of the top level event handlers, which has to be async void
. (But you shouldn't use async void
methods anywhere else, because they can't be await
ed.)
This means your function would become:
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
}
Upvotes: 4
Reputation: 32202
What you want is an extra function to combine the previous tasks
public async Task<string> Combine(Task<string> a, Task<string b){
var aa = await a;
var bb = await b;
return aa + bb;
}
and
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;
}
Note one of the places you really really
have to use blocking calls is in the entry point main function
of the application which can't be declared async
and thus can't use await
Upvotes: 0
Reputation: 2818
Here is some workable code:
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;
}
Upvotes: 1