C# 如何从非异步方法调用异步方法?
我有以下方法:C# 如何从非异步方法调用异步方法?,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我有以下方法: public string RetrieveHolidayDatesFromSource() { var result = this.RetrieveHolidayDatesFromSourceAsync(); /** Do stuff **/ var returnedResult = this.TransformResults(result.Result); /** Where result gets used **/
public string RetrieveHolidayDatesFromSource() {
var result = this.RetrieveHolidayDatesFromSourceAsync();
/** Do stuff **/
var returnedResult = this.TransformResults(result.Result); /** Where result gets used **/
return returnedResult;
}
private async Task<string> RetrieveHolidayDatesFromSourceAsync() {
using (var httpClient = new HttpClient()) {
var json = await httpClient.GetStringAsync(SourceURI);
return json;
}
}
我错过什么了吗
注意:出于某种原因,当我中断上述异步方法时,当它到达行var json=await httpClient.GetStringAsync(SourceURI)
时,它只是超出了断点,我无法返回该方法
public string RetrieveHolidayDatesFromSource() {
var result = this.RetrieveHolidayDatesFromSourceAsync().Result;
/** Do stuff **/
var returnedResult = this.TransformResults(result.Result); /** Where result gets used **/
return returnedResult;
}
如果将.Result添加到异步调用中,它将执行并等待结果到达,从而强制其同步
更新:
private static string stringTest()
{
return getStringAsync().Result;
}
private static async Task<string> getStringAsync()
{
return await Task.FromResult<string>("Hello");
}
static void Main(string[] args)
{
Console.WriteLine(stringTest());
}
private静态字符串stringTest()
{
返回getStringAsync().Result;
}
私有静态异步任务getStringAsync()
{
返回等待任务。FromResult(“Hello”);
}
静态void Main(字符串[]参数)
{
Console.WriteLine(stringTest());
}
针对评论:这项工作没有任何问题 我错过什么了吗 对。异步代码本质上意味着在操作进行时不使用当前线程。同步代码——就其性质而言——意味着当前线程在操作进行时被阻塞。这就是为什么从同步代码中调用异步代码毫无意义。事实上,正如我在博客上描述的那样 首先要考虑的是:我的API应该是同步的还是异步的?如果它处理I/O(如本例中所示),则它将。因此,这将是一个更合适的设计:
public async Task<string> RetrieveHolidayDatesFromSourceAsync() {
var result = await this.DoRetrieveHolidayDatesFromSourceAsync();
/** Do stuff **/
var returnedResult = this.TransformResults(result); /** Where result gets used **/
return returnedResult;
}
公共异步任务检索HolidayDatesFromSourceAsync(){
var result=wait this.DoRetrieveHolidayDatesFromSourceAsync();
/**做事**/
var returnedResult=this.TransformResults(result);/**使用result的位置**/
返回返回结果;
}
正如我在我的中所描述的,您应该“一路异步”。如果您不这样做,您将不会从异步中获得任何好处,那么为什么还要麻烦呢
但假设你最终对异步感兴趣,但现在你不能改变一切,你只想改变你应用的一部分。这是很常见的情况
在这种情况下,正确的方法是公开同步和异步API。最终,在升级所有其他代码之后,可以删除同步API。在我的文章中,我探索了这类场景的各种选择;我个人最喜欢的是“bool参数hack”,它看起来像这样:
public string RetrieveHolidayDatesFromSource() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: true).GetAwaiter().GetResult();
}
public Task<string> RetrieveHolidayDatesFromSourceAsync() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: false);
}
private async Task<string> DoRetrieveHolidayDatesFromSourceAsync(bool sync) {
var result = await this.GetHolidayDatesAsync(sync);
/** Do stuff **/
var returnedResult = this.TransformResults(result);
return returnedResult;
}
private async Task<string> GetHolidayDatesAsync(bool sync) {
using (var client = new WebClient()) {
return sync
? client.DownloadString(SourceURI)
: await client.DownloadStringTaskAsync(SourceURI);
}
}
公共字符串检索HolidayDatesFromSource(){
返回此.DoRetrieveHolidayDatesFromSourceAsync(sync:true).GetAwaiter().GetResult();
}
公共任务检索HolidayDatesFromSourceAsync(){
返回此.DoRetrieveHolidayDatesFromSourceAsync(同步:false);
}
专用异步任务DoRetrieveHolidayDatesFromSourceAsync(布尔同步){
var result=wait this.GetHolidayDatesAsync(同步);
/**做事**/
var returnedResult=this.TransformResults(结果);
返回返回结果;
}
专用异步任务GetHolidayDatesAsync(bool同步){
使用(var client=new WebClient()){
返回同步
?客户端下载字符串(SourceURI)
:wait client.downloadstringtasksync(SourceURI);
}
}
这种方法避免了代码重复,也避免了其他“异步同步”反模式解决方案常见的死锁或重入问题
请注意,我仍然将生成的代码视为通往正确异步API的路径上的“中间步骤”。特别是,内部代码必须依赖于
WebClient
(它支持同步和异步),而不是首选的HttpClient
(它只支持异步)。一旦所有调用代码都更改为使用RetrieveHolidayDatesFromSourceAsync
而不是RetrieveHolidayDatesFromSource
,那么我将重新访问它并删除所有技术债务,将其更改为使用HttpClient
并仅异步。我认为这不起作用,因为我已经在方法中有了“result.result”。使用此.RetrieveHolidayDatesFromSourceAsync().Result将结果更改为非任务对象,这将导致“var returnedResult=this.TransformResults(Result.Result)”中出现错误。我更新了答案以向您展示工作代码。您可以看到getStringAsync是在同步方法(stringTest())中调用的异步方法,我获得了所需的输出,没有错误。您得到的错误是什么?“此操作没有任何问题”:。。。在控制台应用程序之外实际使用时,您可能会更好地解释如何处理由此产生的死锁。如果您在UI环境或任何具有单线程的环境中运行,则可能会有死锁。在这种情况下,对.Result
的调用将阻止进一步的执行,但是async
方法中的wait
可能会在被阻止线程的运行时注册它的继续。混合使用异步和同步等待总是非常危险的,尤其是在UI环境中。它“工作时没有任何问题”这一事实有点侥幸,完全是因为在等待时返回的任务已经完成。任何使用HttpClient
或WebClient
的代码都不会出现这种情况。它是public Task retrieveholidaysfromsourceasync()
缺少wait
?@NickWeaver:不。它不是async
,因此不能使用wait
。我明白了。我想知道,因为该方法的名称在末尾声明为“Async”。为什么不设置为async?@NickWeaver:因为它是一个简单的方法,所以它直接返回任务。它仍然是一个异步API,因此使用了Async
后缀,但它的实现没有使用Async
/wait
。感谢您的解释。
public string RetrieveHolidayDatesFromSource() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: true).GetAwaiter().GetResult();
}
public Task<string> RetrieveHolidayDatesFromSourceAsync() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: false);
}
private async Task<string> DoRetrieveHolidayDatesFromSourceAsync(bool sync) {
var result = await this.GetHolidayDatesAsync(sync);
/** Do stuff **/
var returnedResult = this.TransformResults(result);
return returnedResult;
}
private async Task<string> GetHolidayDatesAsync(bool sync) {
using (var client = new WebClient()) {
return sync
? client.DownloadString(SourceURI)
: await client.DownloadStringTaskAsync(SourceURI);
}
}