如何以 .NET Standard 2.0 普通 synchronous 方式正确提取 HttpResponseMessage
Content
属性(类型为 HttpContent
),在使用异步 await HttpClient.SendAsync(request)
后获取,而不使用死锁危险任务 Result
属性或Wait()
方法?
背景:.NET Standard 2.0 C# 库使用 await HttpClient.SendAsync()
以异步方式使用一些 API,但有时它需要以普通 synchronous 方式按需提取和反序列化响应内容,即通过初始化一些 Lazy 的属性反对它。
API请求是这样的:
HttpResponseMessage response = await HttpClient.SendAsync(request).ConfigureAwait(false);
而相应的异步 Content
提取到字符串操作将是这样的:
string content = await response.Content.ReadAsStringAsync();
然后,惰性属性 (synchronous) 初始化程序将获取字符串结果,如下所示:
string result = (await response.Content.ReadAsStringAsync()).Result;
或者,有时更好:
string result = Task.Run( () => response.Content.ReadAsStringAsync() ).Result;
然而,这两种情况https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d。
挑战:库的建议解决方案是在 synchronous 调用中使用普通的 synchronous 读取,即使用 synchronous response.Content.ReadAsString()
而不是异步 response.Content.ReadAsStringAsync()
。
但是,这种 synchronous 方法不存在,并且没有在 .NET Standard 2.0 HttpContent
类 https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpcontent?view=netstandard-2.0 中描述,尽管它在其他版本(即 https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpcontent?view=net-6.0。
是否可以在 .NET Standard 2.0 中以某种方式实现它,即使用 synchronous StreamReader
和 JsonSerializer
,或任何其他安全方式以及如何实现?
提前致谢。
回答1
如何以 .NET Standard 2.0 普通 synchronous 方式正确提取 HttpResponseMessage Content 属性(类型为 HttpContent),在使用异步等待 HttpClient.SendAsync(request) 后获取,而不使用死锁危险任务 Result 属性也不是 Wait() 方法?
你有两个选择:
- 修复库,使其不再需要 synchronous 代码。
- 以不会导致死锁的方式阻止任务。
第一个选项修复了要求:
它需要以简单的 synchronous 方式按需提取和反序列化响应内容,即通过为其初始化一些 Lazy 对象的属性。
因此,如果您必须异步初始化惰性对象,请更改惰性对象以使其支持异步初始化。例如,使用我的 https://github.com/StephenCleary/AsyncEx 中的 AsyncLazy<T>
,或使用 Lazy<Task<T>>
。
这是最好的解决方案。
API 请求是这样的……然后,惰性属性(synchronous)初始化器会获取这样的字符串结果……
完全不清楚代码将如何将 await
用于 API 请求,同时避免 await
用于读取响应内容,并保持整体 synchronous。
然而,这两种情况都可能导致死锁,具体取决于上下文。
一点也不。 Task.Run
不会死锁。 https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d 在如何使用“死锁”术语方面相当混乱,将其与线程池耗尽混为一谈。我建议不要担心库中的线程池耗尽;如果您必须提供一个 synchronous API,那么该 API 将根据定义阻止调用线程 - 没有办法解决这个问题,因此无需担心。