问题
I can prevent deadlock for Result call for async Task in sync action method of controller using both: ConfigureAwaits(false) on Task or using Task.Run. In both cases async method will be completed on thread from threadpool. Controller source:
public class TestController : Controller
{
/// <summary>
/// Thread from threadpool will be used to finish async method.
/// </summary>
public string TaskRun()
{
return Task.Run(GetStatus).Result + " <br/> " +
Thread.CurrentThread.ManagedThreadId + " - " +
Thread.CurrentThread.IsThreadPoolThread;
}
/// <summary>
/// After completion it will try to finish async method in original thread used to work for httprequest.
/// </summary>
public string Deadlock()
{
return GetStatus().Result;
}
/// <summary>
/// Thread from threadpool will be used to finish async method.
/// </summary>
public string AwaitsFalse()
{
return GetStatusAwaitsFalse().Result + " <br/> " +
Thread.CurrentThread.ManagedThreadId + " - " +
Thread.CurrentThread.IsThreadPoolThread;
}
public async Task<string> PureAsync()
{
return await GetStatus() + " <br/> " +
Thread.CurrentThread.ManagedThreadId + " - " +
Thread.CurrentThread.IsThreadPoolThread;
}
public static async Task<string> GetStatusAwaitsFalse()
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://www.google.com")
.ConfigureAwait(false);
return response.StatusCode + " - " +
Thread.CurrentThread.ManagedThreadId + " - " +
Thread.CurrentThread.IsThreadPoolThread;
}
}
public static async Task<string> GetStatus()
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://www.google.com");
return response.StatusCode + " - " +
Thread.CurrentThread.ManagedThreadId + " - " +
Thread.CurrentThread.IsThreadPoolThread;
}
}
}
Output from /test/taskrun (two last int values are ManagedThreadId and IsThreadPoolThread):
OK - 12 - True
6 - True
Output from /test/awaitsfalse is the same. Is there any difference?
回答1:
There's no universal solution for sync-over-async, only a variety of anti-patterns that each have different problems. For detailed information, see my MSDN article on brownfield async.
The problem with ConfigureAwait(false)
is that it must be applied everywhere - for every await
in the transitive closure of called methods. It's really easy to miss one and then you have a deadlock waiting to happen. For example, last time I checked, HttpClient.GetAsync
is missing one for one of their mobile platform builds. And even if you (and all your dependencies) do this perfectly, it's just as hard to maintain over time.
The problem with Task.Run
is that it runs the code on a thread pool thread. (Obviously). This is fine for code that can run on a thread pool thread, but that's just not the case for all code. It's also less efficient, and (if overused) can cause scalability problems on ASP.NET.
P.S. If you insist on blocking, use GetAwaiter().GetResult()
instead of Result
, because Result
will wrap exceptions in an AggregateException
.
来源:https://stackoverflow.com/questions/40329353/call-async-method-from-sync-action-methods-task-run-or-configureawaitsfalse