Because you aren't using await
with Task.WhenAll(task1, task2);
your program will have issues. If you need to run asynchronous code from within a synchronous context you still need to await
the final result - which you can do with the thread pool and classic synchronization primitives (like ManualResetEvent
) and a "non-blocking" poll loop for safety.
It looks like you want to run two methods concurrently - those methods may or may not be "true" async (i.e. IO-bound, not CPU-bound). In which case use the thread-pool via Task.Run
but that "outer" task itself needs to be run in the pool so that the Task
scheduler.
Rather than simply blocking on Task.Result
, I prefer polling it in a loop with Task.Wait(Int32)
(instead of Task.Wait()
!) which alleviates some deadlocking problems - note that this shares .Result
's problem of making very unproductive use of a program thread. An approach like this should not be used in high-performance, high-demand, or high-throughput applications, especially in ASP.NET WebForms where the (now antiquated and terrible) "one thread per request" model is still being used:
// NASAL DEMON WARNING:
// This method is only intended as a stopgap as this approach does not scale to handling many concurrent entrypoint calls: so it should not be used in *serious business* applications.
private void EntrypointThatWillBlock()
{
Task threadPoolTask = Task.Run( DoConcurrentRequestsAsync );
Boolean didComplete = threadPoolTask.Wait( millisecondsTimeout: 30 * 1000 );
if( !didComplete ) throw new TimeoutException( "Didn't complete in 30 seconds." );
}
private async Task DoConcurrentRequestsAsync()
{
try
{
Task task1 = this.DoExpensieThingAsync();
Task task2 = this.DoAnotherExpensiveThing();
await Task.WhenAll( task1, task2 ).ConfigureAwait(false);
}
catch( Exception ex )
{
// You should log any errors here (and then re-throw) as dealing with unhandled exceptions in async contexts can be a mess. At least by logging here you're guaranteed to get _something_ logged.
this.log.LogError( ex );
throw; // <-- Don't use `throw ex;` as that resets the stack-trace)
}
}
Disclaimer: There likely is a much better way of doing this by using a context-specific synchronization context. As you're using ASP.NET WebForms you can use async
correctly with some configuration tweaks (check your web.config
file, adding Async="true"
to your @Page
directive if applicable, using IHttpAsyncHandler
and/or HostingEnvironment.QueueBackgroundWorkItem
and so on).
Also read What's the meaning of "UseTaskFriendlySynchronizationContext"?