我有一个创建函数的包装器 创建公共异步任务getCacheToken 一些内部服务/应用程序调用
并且正在另一个服务中调用performExtract()来遇到此异常(请参阅以下内容)。
performExtract实际上是通过API调用来调用getCacheToken
我不禁在sync方法(旧版环境)中发送异步调用,因此每次我在循环中调用var results = client.SendAsync(requestData).Result'时,都会导致死锁, 如果我正确理解它,则在send循环内部使用sendAsync,它将在启动另一个任务之前等待任务完成,因此它不应有以下异常(已取消连接?)
要解决此问题,我必须重写send Async ConfigureAwait(false)并解决了我的问题。
我的问题是如何添加ConfigureAwait(false)来解决问题?
为避免此问题,可以使用带有错误参数的名为ConfigureAwait的方法。当您这样做时,这告诉任务可以在任何可用线程上恢复自身,而不用等待最初创建它的线程。这样可以加快响应速度并避免许多死锁。
以及如何异步运行它导致死锁?
非常感谢您的所有患者通过帖子阅读。
protected override ExtractResultStatus PerformExtract()
{
//EngageRestClient client = new EngageRestClient(_apiEndPoint);
//client.Authenticator = new NtlmAuthenticator();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
try
{
var numErrors = 0;
var dt = GetInfos();
var fileName = string.Format(FilenameBase, DateTime.Now);
if (dt.Rows.Count > 0)
{
dt.Columns.Add("failureReason");
foreach (DataRow row in dt.Rows)
{
var referenceID = row["U3l_ReferenceId"].ToString();
var requestData = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(_apiEndPoint + $"?referenceID={referenceID}"),
};
requestData.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
var results = client.SendAsync(requestData).Result;
var resultResponse = results.Content.ReadAsStringAsync().Result;
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
for (int i = 0; i < MaxRetries; i++)
{
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (response.StatusCode != HttpStatusCode.InternalServerError ||
response.StatusCode != HttpStatusCode.NotImplemented ||
response.StatusCode != HttpStatusCode.GatewayTimeout ||
response.StatusCode != HttpStatusCode.ServiceUnavailable)
{
return response;
}
response.Dispose();
}
return response;
}
public async Task<string> GetCacheToken()
{
ObjectCache cache = MemoryCache.Default;
string refreshToken = cache.Get("refreshToken", null) == null ? GetToken() : cache.Get("refreshToken", null).ToString();
if (!cache.Contains("apiToken"))
{
var httpContent = new StringContent("", Encoding.UTF8, "application/x-www-form-urlencoded");
var dict = new Dictionary<string, string>();
dict.Add("grant_type", "refresh_token");
dict.Add("refresh_token", refreshToken);
var requestData = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://oauth2.sky.blackbaud.com/token"),
Content = new FormUrlEncodedContent(dict)
};
requestData.Headers.Authorization = new AuthenticationHeaderValue("Basic", Settings.BasicAuth);
var results = await _client.SendAsync(requestData);
var resultResponse = results.Content.ReadAsStringAsync().Result;
try
{
results.EnsureSuccessStatusCode();
var result = _js.Deserialize<TokenModel>(resultResponse);
//token expires in one hour from blackbaud
var expiration = DateTimeOffset.UtcNow.AddMinutes(55);
cache.Add("apiToken", result.access_token, expiration);
cache.Add("refreshToken", result.refresh_token, expiration);
UpdateToken(result.access_token, result.refresh_token);
}
catch (Exception e)
{
var exceptionMessage = $"ResultMessage : {resultResponse} Exception: {e}. Message: {e.Message}. Stacktrace {e.StackTrace}";
Log.Exception(e,exceptionMessage);
throw;
}
}
return cache.Get("apiToken", null).ToString();
}
{数据:[],HResult:-2146233088,HelpLink:null,InnerException:null, 消息:“响应状态代码不表示成功:400(错误 请求)。,来源:“ System.Net.Http”,StackTrace:“位于 System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\ r \ n位于 RaisersEdge.Infrastructure.Cache.d__2.MoveNext()\ r \ n --- 从先前引发异常的位置开始的堆栈跟踪结束 --- \ r \ n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \ n
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)\ r \ n RaisersEdge.Controllers.BaseController.d__6.MoveNext()\ r \ n --- 从先前引发异常的位置开始的堆栈跟踪结束 --- \ r \ n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \ n
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)\ r \ n System.Threading.Tasks.TaskHelpersExtensions.d__3`1.MoveNext()\ r \ n --- 从先前引发异常的位置开始的堆栈跟踪结束 --- \ r \ n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \ n
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)\ r \ n System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\ r \ n --- 从先前引发异常的位置开始的堆栈跟踪结束 --- \ r \ n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \ n
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)\ r \ n System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\ r \ n --- 从先前引发异常的位置开始的堆栈跟踪结束 --- \ r \ n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \ n
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)\ r \ n System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()“, TargetSite:“ System.Net.Http.HttpResponseMessage sureSuccessStatusCode()“,_ typeTag:” HttpRequestException“}
答案 0 :(得分:1)
正如其他人所提到的,您应该不要使用.Result
,因为它是evil!但是如果您使用的是旧版应用,则可以使用以下解决方法:
using System.Threading.Tasks;
public class AsyncHelper
{
private static readonly TaskFactory _taskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TReturn RunSync<TReturn>(Func<Task<TReturn>> task)
{
return _taskFactory.StartNew(task)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
然后,您可以使用帮助程序轻松调用方法:
var results = AsyncHelper.RunSync<System.Net.Http.HttpResponseMessage>(
() => client.SendAsync(requestData)
);
helper类创建,配置并运行一个异步任务,然后对其进行解包并同步等待以获取结果:这几乎是await所做的,这种方法将防止死锁,可以在try / catch块中使用
当然,调用async
方法的唯一正确方法是使用await
,但是当您无法在sync方法中调用async
方法时,此解决方法是更好的选择。
答案 1 :(得分:1)
如何异步运行它会导致死锁?
await
by default captures a context and resumes executing the async
method in that context。如果此上下文一次只允许一个线程,并且调用代码通过调用Result
或Wait
然后是the code causes a deadlock since the async
method cannot resume(和因此无法完成。
如何添加ConfigureAwait(false)解决问题?
因为await
不再捕获其上下文。 async
方法可以在任何线程池线程上恢复,并且不受上下文中阻塞的线程的影响。 ConfigureAwait(false)
is generally considered a best practice for library code。
其中一项服务位于旧版环境中,因此无法调用等待,因为我没有异步函数可以覆盖
有一个variety of hacks you can use to attempt to block on asynchronous code safely。他们都不在任何情况下都能工作。如果ConfigureAwait(false)
为您工作,那我会用。