如何从非异步方法调用异步方法? [英] How Do I Call an Async Method from a Non-Async Method?

查看:31
本文介绍了如何从非异步方法调用异步方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下方法:

    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;
        }
    }

以上不起作用,似乎没有正确返回任何结果.我不确定在哪里缺少强制等待结果的语句?我希望 RetrieveHolidayDatesFromSource() 方法返回一个字符串.

The above does not work and seems to not return any results properly. I am not sure where I am missing a statement to force the await of a result? I want the RetrieveHolidayDatesFromSource() method to return a string.

以下工作正常,但它是同步的,我相信它可以改进吗?请注意,以下是同步的,我想更改为异步,但由于某种原因无法解决.

The below works fine but it is synchronous and I believe it can be improved upon? Note that the below is synchronous in which I would like to change to Asynchronous but am unable to wrap my head around for some reason.

    public string RetrieveHolidayDatesFromSource() {
        var result = this.RetrieveHolidayDatesFromSourceAsync();
        /** Do Stuff **/

        var returnedResult = this.TransformResults(result); /** This is where Result is actually used**/
        return returnedResult;
    }


    private string RetrieveHolidayDatesFromSourceAsync() {
        using (var httpClient = new HttpClient()) {
            var json = httpClient.GetStringAsync(SourceURI);
            return json.Result;
        }
    }

我错过了什么吗?

注意:出于某种原因,当我对上述异步方法进行断点时,当它到达 var json = await httpClient.GetStringAsync(SourceURI) 行时,它只是超出了断点,我可以't 回到方法中.

Note: For some reason, when I breakpoint the above Async Method, when it gets to the line var json = await httpClient.GetStringAsync(SourceURI) it just goes out of breakpoint and I can't go back into the method.

推荐答案

我错过了什么吗?

Am I missing something?

是的.异步代码 - 就其性质而言 - 意味着在操作进行时不使用当前线程.同步代码 - 就其性质而言 - 意味着当前线程在操作进行时被阻塞.这就是为什么从同步代码中调用异步代码实际上甚至没有意义.事实上,正如我在我的博客中所描述的,一种天真的方法(使用 结果/Wait)很容易导致死锁.

Yes. Asynchronous code - by its nature - implies that the current thread is not used while the operation is in progress. Synchronous code - by its nature - implies that the current thread is blocked while the operation is in progress. This is why calling asynchronous code from synchronous code literally doesn't even make sense. In fact, as I describe on my blog, a naive approach (using Result/Wait) can easily result in deadlocks.

首先要考虑的是:我的 API 应该是同步的还是异步的?如果它处理 I/O(如本例中),它应该是异步的.所以,这将是一个更合适的设计:

The first thing to consider is: should my API be synchronous or asynchronous? If it deals with I/O (as in this example), it should be asynchronous. So, this would be a more appropriate design:

public async Task<string> RetrieveHolidayDatesFromSourceAsync() {
    var result = await this.DoRetrieveHolidayDatesFromSourceAsync();
    /** Do stuff **/
    var returnedResult  = this.TransformResults(result); /** Where result gets used **/
    return returnedResult;
}

正如我在我的异步最佳实践文章中所述,您应该选择异步所有大大地".如果不这样做,无论如何你都不会从异步中获得任何好处,那何必呢?

As I describe in my async best practices article, you should go "async all the way". If you don't, you won't get any benefit out of async anyway, so why bother?

但是假设您对最终异步感兴趣,但是现在您不能改变一切,您只想改变部分 您的应用程序.这是一种很常见的情况.

But let's say that you're interested in eventually going async, but right now you can't change everything, you just want to change part of your app. That's a pretty common situation.

在这种情况下,正确的方法是公开同步和异步 API.最终,在升级所有其他代码后,可以删除同步 API.我在我的关于棕地异步开发的文章中探索了这种场景的各种选项;我个人最喜欢的是bool parameter hack",它看起来像这样:

In that case, the proper approach is to expose both synchronous and asynchronous APIs. Eventually, after all the other code is upgraded, the synchronous APIs can be removed. I explore a variety of options for this kind of scenario in my article on brownfield async development; my personal favorite is the "bool parameter hack", which would look like this:

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);
  }
}

这种方法避免了代码重复,还避免了其他同步过异步"反模式解决方案中常见的任何死锁或重入问题.

This approach avoids code duplication and also avoids any deadlock or reentrancy problems common with other "sync-over-async" antipattern solutions.

请注意,我仍会将生成的代码视为通向正确异步 API 的路径上的中间步骤".特别是,内部代码必须依靠 WebClient(支持同步和异步)而不是首选的 HttpClient(仅支持异步).一旦所有调用代码都更改为使用 RetrieveHolidayDatesFromSourceAsync 而不是 RetrieveHolidayDatesFromSource,然后我会重新访问它并删除所有技术债务,将其更改为使用 HttpClient 并且仅异步.

Note that I would still treat the resulting code as an "intermediate step" on the path to a properly-asynchronous API. In particular, the inner code had to fall back on WebClient (which supports both sync and async) instead of the preferred HttpClient (which only supports async). Once all the calling code is changed to use RetrieveHolidayDatesFromSourceAsync and not RetrieveHolidayDatesFromSource, then I'd revisit this and remove all the tech debt, changing it to use HttpClient and be async-only.

这篇关于如何从非异步方法调用异步方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆