C# 使用ConfigureAwait(false)和async Task和await传播异常吗?
我觉得我对异步等待编程有着很好的掌握,但今天发生的事情让我感到困惑。我已经寻找了一段时间的答案,但在任何地方都找不到。我已经读了很多关于异步等待编程的书,但是我虚弱的头脑无法理解在这个特定场景中到底发生了什么 我有两种方法:C# 使用ConfigureAwait(false)和async Task和await传播异常吗?,c#,asynchronous,async-await,configureawait,C#,Asynchronous,Async Await,Configureawait,我觉得我对异步等待编程有着很好的掌握,但今天发生的事情让我感到困惑。我已经寻找了一段时间的答案,但在任何地方都找不到。我已经读了很多关于异步等待编程的书,但是我虚弱的头脑无法理解在这个特定场景中到底发生了什么 我有两种方法: public async Task<IEnumerable<ServerStatus>> GetServersStatusAsync() { var serverStatuses = new List<ServerStatus>(
public async Task<IEnumerable<ServerStatus>> GetServersStatusAsync()
{
var serverStatuses = new List<ServerStatus>();
try
{
ServerStatusRequest request = new ServerStatusRequest();
var serverStatusResponse = await GetAsync<ServerStatusResponse>(request).ConfigureAwait(false);
}
// I would expect the exception thrown from GetAsync to be caught here. But it doesn't always do that.
catch (Exception ex)
{
_log.Error("Failed to retrieve server statuses.", ex);
}
return serverStatuses;
}
如何调用
GetServersStatusAsync
?@TheodorZoulias我这样称呼它:var serverStatus=await GetServersStatusAsync();理想情况下,如果以异步方式抛出异常,则该异常将被抛出到某个线程池
线程上,该线程本应执行等待线程的继续。在上述情况下,它必须异步抛出(在调用await
之后),并且由于您正在附加ConfigureAwait(false),因此它不会被捕获到同一线程上下文中。更多信息:@SaiGummaluri是的,但是try块是上下文吗?我认为即使在不同的上下文中,它仍然可以捕获异常?@DainiusCerniauskas回调有什么意义?.Invoke(result)
当您已经使用async/wait
时<代码>等待将执行返回到原始同步上下文。如果从UI线程调用了Login
,则只需调用callback(result)
。如果必须使用Invoke
,则表示您正在从后台线程调用Login
。您可能是从异步void
方法调用Login
吗?如何调用GetServersStatusAsync
?@TheodorZoulias我这样称呼它:var serverStatus=await GetServersStatusAsync();理想情况下,如果以异步方式抛出异常,则该异常将被抛出到某个线程池
线程上,该线程本应执行等待线程的继续。在上述情况下,它必须异步抛出(在调用await
之后),并且由于您正在附加ConfigureAwait(false),因此它不会被捕获到同一线程上下文中。更多信息:@SaiGummaluri是的,但是try块是上下文吗?我认为即使在不同的上下文中,它仍然可以捕获异常?@DainiusCerniauskas回调有什么意义?.Invoke(result)
当您已经使用async/wait
时<代码>等待将执行返回到原始同步上下文。如果从UI线程调用了Login
,则只需调用callback(result)
。如果必须使用Invoke
,则表示您正在从后台线程调用Login
。您是否正在从async void
方法调用Login
?
private async Task<T> GetAsync<T>(IRequest request)
{
string respString = string.Empty;
try
{
var requestUrl = request.GetQueryString(_apiToken);
_log.Debug($"Sending a request to {requestUrl}");
CancellationTokenSource tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(_timeoutInSec));
//This call can throw an exception. It will always get caught inside this current try catch,
//but if after the await we are continuing on a different thread,
//the re-thrown/propagated exception is not caught by the calling method.
var response = await _client.GetAsync(requestUrl, tokenSource.Token).ConfigureAwait(false);
if (response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.BadRequest)
{
respString = await response.Content.ReadAsStringAsync();
var deserializedObject = JsonConvert.DeserializeObject<T>(respString);
return deserializedObject;
}
}
catch (Exception ex) when (ex is JsonReaderException || ex is JsonSerializationException)
{
throw new JsonSerializationException($"Failed to deserialize {respString}", ex) { Source = respString };
}
return default(T);
}
public async Task Login(Action<LoginResult> callback = null)
{
LoginResult result = new LoginResult() { Success = false };
try
{
var serverStatus = await GetServersStatusAsync();
if (serverStatus.Any())
{
result.Success = true;
callback?.Invoke(result);
return;
}
}
catch (Exception ex)
{
result.Message = Strings.UnknownException;
_log.Error("Login failed due to unexpected exception", ex);
}
callback?.Invoke(result);
}
_restClient.Login(OnLoginResponse);