c# 非async方法调用async方法

勇哥注:


此文作者调研了许多国外大神的文章写下的记录,内含外链,特转载涨知识。



在C#中,强烈不建议非async方法调用async方法,建议一路async/await下去。如果一定要非async方法调用async方法,建议按照以下的优先级使用:


1. Task.Run(...).Result, 这种方式是新起了一个Task放在线程池中,参考: 


https://devblogs.microsoft.com/pfxteam/should-i-expose-synchronous-wrappers-for-asynchronous-methods/    一文中


Offload to Another Thread


Consider offloading to a different thread, which is typically possible unless the method you’re invoking has some kind of thread affinity (e.g. it accesses UI controls). Let’s say you have methods like the following:


int Sync() // caller needs this to return synchronously 

    return Library.FooAsync().Result; 

}


// in a library; uses await without ConfigureAwait(false) 

public static Task<int> FooAsync();


As described above, FooAsync is using await without a ConfigureAwait(false), and as you don’t own the code, you’re unable to fix that. Further, the Sync method you’re implementing is being called from the UI thread, or more generally from a context prone to deadlocking due to a limited number of participating threads (in the case of the UI, that limited number is one). Solution? Ensure that the await in the FooAsync method doesn’t find a context to marshal back to. The simplest way to do that is to invoke the asynchronous work from the ThreadPool, such as by wrapping the invocation in a Task.Run, e.g.


int Sync() 

    return Task.Run(() => Library.FooAsync()).Result; 

}


FooAsync will now be invoked on the ThreadPool, where there won’t be a SynchronizationContext, and the continuations used inside of FooAsync won’t be forced back to the thread that’s invoking Sync().


2. .GetAwaiter.GetResult(): 这种方式比Task.Result好,但是它仍会有潜在死锁的问题,参考:


https://stackoverflow.com/questions/17284517/is-task-result-the-same-as-getawaiter-getresult



Task.GetAwaiter().GetResult() is preferred over Task.Wait and Task.Result because it propagates exceptions rather than wrapping them in an AggregateException. However, all three methods cause the potential for deadlock issues and should be avoided in favor of async/await.


The quote below explains why Task.Wait and Task.Result don't simply contain the exception propagation behavior of Task.GetAwaiter().GetResult() (due to a "very high compatibility bar").


As I mentioned previously, we have a very high compatibility bar, and thus we’ve avoided breaking changes. As such, Task.Wait retains its original behavior of always wrapping. However, you may find yourself in some advanced situations where you want behavior similar to the synchronous blocking employed by Task.Wait, but where you want the original exception propagated unwrapped rather than it being encased in an AggregateException. To achieve that, you can target the Task’s awaiter directly. When you write “await task;”, the compiler translates that into usage of the Task.GetAwaiter() method, which returns an instance that has a GetResult()method. When used on a faulted Task, GetResult() will propagate the original exception (this is how “await task;” gets its behavior). You can thus use “task.GetAwaiter().GetResult()” if you want to directly invoke this propagation logic.


https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/


“GetResult” actually means “check the task for errors”


In general, I try my best to avoid synchronously blocking on an asynchronous task. However, there are a handful of situations where I do violate that guideline. In those rare conditions, my preferred method is GetAwaiter().GetResult() because it preserves the task exceptions instead of wrapping them in an AggregateException.


http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html


3. 不要在同步方法里调用异步方法的.Result,这是因为当前的主线程和异步方法的Context有可能会相互等,造成死锁,Task.WhenAll可能是新起一个Task放在ThreadPool里,Task.WaitAll应该是Block当前,参考:


http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html



What Causes the Deadlock

Here’s the situation: remember from my intro post that after you await a Task, when the method continues it will continue in a context.


In the first case, this context is a UI context (which applies to any UI except Console applications). In the second case, this context is an ASP.NET request context.


One other important point: an ASP.NET request context is not tied to a specific thread (like the UI context is), but it does only allow one thread in at a time. This interesting aspect is not officially documented anywhere AFAIK, but it is mentioned in my MSDN article about SynchronizationContext.


So this is what happens, starting with the top-level method (Button1_Click for UI / MyController.Get for ASP.NET):


The top-level method calls GetJsonAsync (within the UI/ASP.NET context).

GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).

GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.

GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.

The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.

… Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.

The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.

Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete.

For the UI example, the “context” is the UI context; for the ASP.NET example, the “context” is the ASP.NET request context. This type of deadlock can be caused for either “context”.


————————————————

版权声明:本文为CSDN博主「taoqick」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/taoqick/article/details/89383112



本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年3月    »
123
45678910
11121314151617
18192021222324
25262728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864