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

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

问题描述

我有以下方法:

 公共字符串RetrieveHolidayDatesFromSource(){
        VAR的结果= this.RetrieveHolidayDatesFromSourceAsync();
        /** 做东西 **/
        VAR returnedResult = this.TransformResults(result.Result); / **凡结果被使用** /
        返回returnedResult;
    }
    私人异步任务<串GT; RetrieveHolidayDatesFromSourceAsync(){
        使用(VAR的HttpClient =新的HttpClient()){
            VAR JSON =等待httpClient.GetStringAsync(SourceURI);
            返回JSON;
        }
    }

以上不工作,似乎无法正常返回任何结果。我不知道我在哪里丢失的一份声明中强制结果的的await?我想RetrieveHolidayDatesFromSource()方法返回一个字符串。

下面的工作正常,但它是同步的,我相信它可以改进?请注意,以下是同步的,我想更改为异步但我无法绕到我的头出于某种原因。

 公共字符串RetrieveHolidayDatesFromSource(){
        VAR的结果= this.RetrieveHolidayDatesFromSourceAsync();
        /** 做东西 **/        VAR returnedResult = this.TransformResults(结果); / **这是结果实际使用** /
        返回returnedResult;
    }
    私人字符串RetrieveHolidayDatesFromSourceAsync(){
        使用(VAR的HttpClient =新的HttpClient()){
            VAR JSON = httpClient.GetStringAsync(SourceURI);
            返回json.Result;
        }
    }

我缺少的东西吗?

请注意:由于某些原因,当我断点上述异步方法,当它到达行VAR JSON =等待httpClient.GetStringAsync(SourceURI),它只是出去断点,我不能再进去方法。


解决方案

  

我缺少的东西吗?


是的。异步code - 其本质 - 意味着当前线程不使用,而操作正在进行中。同步code - 其本质 - 意味着,虽然操作过程中,当前线程被阻塞。这就是为什么从调用同步code异步code字面上甚至没有任何意义。事实上,我描述了我的博客上,一个天真的做法(使用结果 / 等待)很容易导致死锁的。

要考虑的第一件事是:的我的API是同步或异步?如果它与我涉及输入/输出(如在本实施例),它应当异步。因此,这将是一个更合适的设计:

 公共异步任务<串GT; RetrieveHolidayDatesFromSourceAsync(){
    VAR的结果=等待this.DoRetrieveHolidayDatesFromSourceAsync();
    /** 做东西 **/
    VAR returnedResult = this.TransformResults(结果); / **凡结果被使用** /
    返回returnedResult;
}

我在异步最佳实践文章形容,你应该去异步所有办法。如果不这样做,你不会得到任何好处出异步反正,何必呢?

但是,让我们说,你有兴趣的最后的异步去,但是现在你不能改变的所有的,你只是想改变的部分< /你的应用程序的EM>。这是一个pretty常见的情况。

在这种情况下,适当的方法是,以暴露的两个的同步和异步的API。最终,所有其他的code升级后,同步的API可以被删除。我探索了多种选择这类在棕地开发异步我文章场景;我个人最喜欢的是布尔参数黑客,这将是这样的:

 公共字符串RetrieveHolidayDatesFromSource(){
  返回this.DoRetrieveHolidayDatesFromSourceAsync(同步:真).GetAwaiter()调用getResult();
}公共任务&LT;串GT; RetrieveHolidayDatesFromSourceAsync(){
  返回this.DoRetrieveHolidayDatesFromSourceAsync(同步:假);
}私人异步任务&LT;串GT; DoRetrieveHolidayDatesFromSourceAsync(布尔同步){
  VAR的结果=等待this.GetHolidayDatesAsync(同步);
  /** 做东西 **/
  VAR returnedResult = this.TransformResults(结果);
  返回returnedResult;
}私人异步任务&LT;串GT; GetHolidayDatesAsync(布尔同步){
  使用(VAR的客户=新的WebClient()){
    返回同步
        ? client.DownloadString(SourceURI)
        :等待client.DownloadStringTaskAsync(SourceURI);
  }
}

该方法避免了code重复,也避免了与其他同步过异步反模式的解决方案常见的死锁或重入的问题。

请注意,我仍然会像对待所产生的code作为道路上的一个中间步骤,以正确异步API。尤其是,内code只好退到 Web客户端(同​​时支持同步和异步),而不是preferred 的HttpClient (只支持异步)。一旦所有的调用code更改为使用 RetrieveHolidayDatesFromSourceAsync ,而不是 RetrieveHolidayDatesFromSource ,那么我会重新考虑这一点,去掉所有的高科技债务,将其更改为使用的HttpClient 并异步只。

I have the below method:

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

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

Am I missing something?

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?

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.

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.

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.

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天全站免登陆