The*_*yer 2 c# .net-4.5 asp.net-4.5
我的代码内部有一个 PostAsync 方法,它似乎永远不会返回响应。但是,我通过 同步使用它.GetAwaiter().GetResult()。目标框架是net45.
public async Task<TResponse> PostAsync<TResponse, TRequest>(string method, TRequest body)
{
_logger.Log($"Method {method}, body {JsonConvert.SerializeObject(body)} on url {_configuration.ApiUrl}");
using (var customDelegatingHandler = new HMACDelegatingHandler(_configuration, _apiId))
{
using (var client = new HttpClient(customDelegatingHandler))
{
var response = await client.PostAsync($"{_configuration.ApiUrl}/{method}",
new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResponse>(content);
}
else
{
await Log(body, response);
}
return default;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我所做的是在另一个方法中调用 PostAsync:
public async Task<decimal> GetBalance(Request request)
{
// = new MyCustomClient...
QueryFundsResponse response = await customClient.PostAsync<Response, Request>("testAction", request);
if (response == default)
return 0.0m;
return response.Amount;
}
Run Code Online (Sandbox Code Playgroud)
然后,最后在流程的最顶部,我像这样调用 GetBalance 方法:
var sw = new Stopwatch();
sw.Start();
var balance = _provider
.GetBalance(request)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
sw.Stop();
_logger.Log($"GetBalance -> method duration: { sw.ElapsedMilliseconds }");
Run Code Online (Sandbox Code Playgroud)
我根本没有在日志中看到日志,而且我似乎没有得到响应或在.GetAwaiter().GetResult(). 遗憾的是,将最后一个代码块切换为异步并且await该GetBalance()方法对我来说并不是一个真正的选择。
我无法弄清楚为什么即使在使用该.ConfigureAwait(false)方法之后也没有任何改变。
您遇到了阻塞异步代码时发生的常见死锁(我的博客中有详细描述)。有多种方法可以解决这个问题,但它们都是黑客技术,而且没有一种方法适用于所有情况。
在你的情况下,我会说要么使用直接阻止 hack要么使用布尔参数 hack。
直接阻止黑客需要你到处使用ConfigureAwait(false)。请注意,您当前的代码仅在不ConfigureAwait(false)执行任何操作的情况下使用;配置,因此它需要转到s 所在的位置。他们全部。ConfigureAwaitawaitawait
布尔参数 hack 意味着您的代码将采用一个bool参数来确定它是同步执行还是异步执行。请注意HttpClient(目前)有一个仅异步 API,因此您的自定义委托处理程序将需要支持直接阻塞,使用ConfigureAwait(false). 同样,Log要么需要同步等效项,要么也支持直接阻塞。你的代码最终会看起来像这样:
public Task<TResponse> PostAsync<TResponse, TRequest>(string method, TRequest body) => PostCoreAsync(method, body, sync: false);
public TResponse Post<TResponse, TRequest>(string method, TRequest body) => PostCoreAsync(method, body, sync: true).GetAwaiter().GetResult();
private async Task<TResponse> PostCoreAsync<TResponse, TRequest>(string method, TRequest body, bool sync)
{
_logger.Log($"Method {method}, body {JsonConvert.SerializeObject(body)} on url {_configuration.ApiUrl}");
using (var customDelegatingHandler = new HMACDelegatingHandler(_configuration, _apiId))
{
using (var client = new HttpClient(customDelegatingHandler))
{
var responseTask = client.PostAsync($"{_configuration.ApiUrl}/{method}",
new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"));
var response = sync ? responseTask.GetAwaiter().GetResult() : await responseTask;
if (response.StatusCode == HttpStatusCode.OK)
{
var content = sync ? response.Content.ReadAsStringAsync().GetAwaiter().GetResult() : await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResponse>(content);
}
else
{
var logTask = Log(body, response);
if (sync)
logTask.GetAwaiter().GetResult();
else
await logTask;
}
return default;
}
}
}
public Task<decimal> GetBalanceAsync(Request request) => GetBalanceCoreAsync(request, sync: false);
public decimal GetBalance(Request request) => GetBalanceCoreAsync(request, sync: true).GetAwaiter().GetResult();
private async Task<decimal> GetBalanceCoreAsync(Request request, bool sync)
{
// = new MyCustomClient...
QueryFundsResponse response = sync ?
customClient.Post<Response, Request>("testAction", request) :
await customClient.PostAsync<Response, Request>("testAction", request);
if (response == default)
return 0.0m;
return response.Amount;
}
var sw = new Stopwatch();
sw.Start();
var balance = _provider
.GetBalance(request);
sw.Stop();
_logger.Log($"GetBalance -> method duration: { sw.ElapsedMilliseconds }");
Run Code Online (Sandbox Code Playgroud)