在同步 class 库中使用 HttpClient

Using HttpClient in the synchronous class library

我正在为第 3 方 API 编写一个 class 库包装器,使用 .Net Standard,稍后我打算在我的其他项目中使用这个包装器。在浏览网络时,我发现人们普遍关心的是应该使用 HttpClient class 来发出 HTTP 请求。

我知道我可以采取两种方法:

  1. 使用 async/await 一直到客户端项目
  2. 使用 Task.Wait() 方法

到目前为止,我将采用第一种方法。但这两种方法似乎都有问题。第一个比同步方法的可重用性低,同步方法 return 它们的类型而不是 Task 对象。我宁愿采用第二种方法,但它容易出现死锁。

我发出单个请求的代码(使用无参数构造函数初始化的 HttpClient):

    protected async Task<JObject> Request(string url)
    {
        Uri uri = BuildUrl(url);
        HttpResponseMessage response = await HttpClient.GetAsync(uri);
        if (response.IsSuccessStatusCode)
        {
            string result = await response.Content.ReadAsStringAsync();
            return JObject.Parse(result);
        }

        return new JObject();
    }

对于多个请求:

    protected async Task<JObject[]> Request(IEnumerable<string> urls)
    {
        var requests = urls.Select(Request);
        return await Task.WhenAll(requests);
    }

包装器中的用法class:

    protected async Task<JObject> RequestGet(string id, bool byUrl)
    {
        if (IsBulk && !byUrl)
            return await Request($"{Url}?id={id}");

        if (byUrl)
            return await Request(Url + id);

        return await Request(Url);
    }

我如何修改代码(第 1 和第 2 个代码段),以便它不会在每个调用方方法(第 3 个代码段)中导致任何死锁和异步使用?

一直使用 async 是正确的方法,您可以 return 从 Task 方法中得到结果(换句话说——如您自己的代码所示——说他们 return 除了 Task 什么都不做是不对的:

public async Task<string> GetString(int value)
{
    return value.ToString();
}

显然不需要 async,它不需要 await 任何东西,但关键是 Task 可以 return 任何东西感谢仿制药。您甚至可以使用新奇的 C# 7 值元组:

public async Task<(string name, int age)> GetUserInfo(int userId) { ... }

不支持同步使用 HttpClient 并且容易出现死锁,您无法执行任何操作(包括使用 Task.Wait)来改变这一事实。您有 2 个选择:

  1. 仅支持异步。

  2. 改用旧的 WebRequest APIs 并以这种方式支持同步调用。

我会选择选项 1。最新最好的 HTTP 库甚至不再支持同步 I/O 是有原因的。较新的多核处理器和分布式架构浪潮引发了编程世界的范式转变,在等待 I/O 时占用线程(尤其是更长的 运行 网络调用,如 HTTP)总计资源浪费。当像 C# 这样的语言为开箱即用的异步编程提供令人难以置信的支持时,几乎没有充分的理由再进行同步 HTTP。大多数开发人员都明白这一点,我认为您不应该担心仅通过异步来放弃用户。相反,鼓励他们以正确的方式加快速度。