【问题标题】:C# HttpClient GetStringAsync Task returns earlyC# HttpClient GetStringAsync 任务提前返回
【发布时间】:2016-05-01 14:57:48
【问题描述】:

我正在处理一个 ASP.NET 项目,我正在使用我们创建的自定义 REST 服务来获取该站点的 JSON 数据。我正在使用 HTTPClient 来调用 REST 服务。但是当我调用我在下面创建的 ProcessRestMethod 例程时,有时任务会返回正确的结果,但有时任务不会完成执行并且例程会返回。我在异步调用中缺少什么?我的 ProcessRestMethod 代码如下。谢谢:

private string ProcessRestMethod(string methodName, string parameters)
{
    string result = "";
    using (HttpClient httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        string strParams = parameters;

        if ((!String.IsNullOrEmpty(parameters)) && (parameters.IndexOf('?') != 0))
            strParams = "?" + parameters;


        var task = httpClient.GetStringAsync(baseUri + methodName + strParams);
        task.Wait();
        task.ContinueWith((t) =>
        {
            var tresult = t.Result;
            result = tresult;
        });
    }
    if (result == "")
        result = "{\"status\":{\"code\":1000,\"message\":\"Unkown Error Ocured\"}}";
    return result;
}

【问题讨论】:

  • 我不确定您为什么要以如此复杂的方式执行此操作,但无论如何,您永远不会等待继续。有时,它可能会在您到达 if (result == "") 之前运行,有时则不会。
  • 你为什么不用async/await?您现在所做的只是浪费 两个 线程,而不是让 ASP.NET 在执行 GetStringAsync 时重用原始线程
  • 此外,如果您关心错误,请使用GetAsync 并检查响应状态。在绝大多数情况下,这些都是众所周知的错误,例如路径错误、未经授权等
  • @PanagiotisKanavos:如果代码未经授权、404 等(配置错误)编码,则代码不太可能自行纠正。因此,获取特定异常无济于事。其次,IIRC HttpClient 只有一个异步接口,因此必须使用该功能。使用它并不意味着 async/await 是最好的解决方案(如果调用树顶部的所有内容都是同步的)
  • @jgauffin 首先,没有说任何关于异常的事情。其次,404 不是配置 错误——它可能意味着文件还没有。或者 403 可能意味着使用了错误的帐户。或 429 - 请求过多等。忽略这些并返回“未知错误”几乎是一个错误。关于接口的评论也不正确 - 使用 HttpClient 意味着您已经异步。在任何情况下,决定async 是否是一个好选择的是下面的内容。如果上面的代码不是异步的,你可以选择模仿同步执行,或者修改它以利用异步

标签: c# asp.net async-await httpclient dotnet-httpclient


【解决方案1】:

您需要做的就是获取任务的 Result 属性 - 这将等待它完成并返回结果。

您之前所做的是启动“延续”任务以获得结果。这将导致在不同的线程中获得结果,因此您有您描述的竞争条件。

  private string ProcessRestMethod(string methodName, string parameters)
  {
        string result = "";
        using (HttpClient httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            string strParams = parameters;

            if ((!String.IsNullOrEmpty(parameters)) && (parameters.IndexOf('?') != 0))
                strParams = "?" + parameters;


            var task = httpClient.GetStringAsync(baseUri + methodName + strParams);
            // Just call `task.Result` here :)
            result = task.Result;
        }
        if (result == "")
            result = "{\"status\":{\"code\":1000,\"message\":\"Unkown Error Ocured\"}}";
        return result;
    }

【讨论】:

  • 是的。不过,我现在更喜欢.GetAwaiter().GetResult(); - 它更好地保留了异常信息。
  • 向 OP 展示如何使用 async/await 重写代码而不是阻塞任务并冒死锁风险不是更好吗?该代码几乎是what not to do with Tasks 的惯用示例。
  • @spender 我倾向于按要求回答问题。将给定的方法重写为异步的问题是他会立即从调用代码中得到一个编译错误,因为该方法将返回一个任务,而不是一个字符串。我同意他需要了解 Tasks 如何更好地工作,但我并不完全相信 SO 答案一定是适合它的地方。真的,我也应该教他什么是竞争条件,这意味着教他什么是线程,这意味着教他什么是进程,这意味着......(重复和恶心)
  • 您在 using 块范围内重新声明 result。这不应该编译。
  • 好地方,谢谢 - 我错过了,因为它被缺少 baseUri 变量的编译器错误所掩盖,但这是来自他的代码。
【解决方案2】:

使用

尝试以下操作

异步/等待

    private async Task<string> ProcessRestMethod(string methodName, string parameters)
    {
        string result = "";
        using (HttpClient httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            string strParams = parameters;

            if ((!String.IsNullOrEmpty(parameters)) && (parameters.IndexOf('?') != 0))
                strParams = "?" + parameters;


            result = await httpClient.GetStringAsync(baseUri + methodName + strParams);

        }
        if (result == "")
            result = "{\"status\":{\"code\":1000,\"message\":\"Unkown Error Ocured\"}}";
        return result;
    }

您可以在此处阅读有关任务、异步和等待的更多信息 https://www.simple-talk.com/dotnet/.net-tools/c-async-what-is-it,-and-how-does-it-work/ http://www.dotnetperls.com/async 重要的是,这个解释了最好的 https://msdn.microsoft.com/library/hh191443.aspx

【讨论】:

  • 那不会编译:The return type of an async method must be void, Task or Task&lt;T&gt;
  • -1 语法不正确,您不能仅仅假设该方法可以异步,这取决于应用程序的类型和调用树。
  • @jgauffin 实际上你可以假设如果该方法包含至少一个异步调用,则可以将其设为异步。使其“同步”是需要努力的,并且可能会导致并发症
  • @PanagiotisKanavos:当 Async 方法是类 API 中唯一可用的方法时,则不会。我很高兴您从未见过任何开发人员以非预期的方式使用 API。假设 OP 代码是什么样的只是猜测。
  • @jgauffin 相反,我已经看到网络农场融化了,因为开发人员调用了Wait() 而不是使用async,没有意识到阻塞实际上是从一个 SpinWait() 开始,然后才真正阻塞线程.因此。 CPU 猛增,项目仍在队列中,最终 IIS 回收在高峰时段在整个场中波及。你所说的“同步”实际上是伪造的。不是 API 的问题,是对网络调用、线程池的行为等有什么误解。最终所有的 I/O 操作在驱动层面都是异步的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-28
  • 1970-01-01
相关资源
最近更新 更多