我有一个包装器,它创建了一个函数--公共异步任务getCacheToken --为一些内部服务/应用程序调用
通过在另一个服务中调用performExtract(),我正在经历这个异常(请参见下面的内容)。
performExtract实际上是通过API调用调用getCacheToken。
我无法帮助在同步方法(遗留环境)内发送异步调用,所以每次在循环中调用var results = client.SendAsync(requestData).Result‘时,都会导致死锁,如果我正确理解它,for循环中的sendAsync将在启动另一个任务之前等待任务完成,因此它不应该有以下异常(处理连接?)
要修复它,我必须重写发送异步ConfigureAwait(false),它解决了我的问题。
我的问题是添加ConfigureAwait(false)如何解决这个问题?
为了避免这个问题,您可以使用一个名为ConfigureAwait的方法,其中包含一个false参数。当您这样做时,这会告诉Task,它可以在任何可用的线程上恢复自身,而不是等待最初创建它的线程。这将加快响应速度,避免许多死锁。
运行异步是如何导致死锁的?
非常感谢你所有的耐心阅读通过这篇文章。
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,Message:“响应状态代码不表示成功: 400 (坏请求)。”,源:"System.Net.Http",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(Task任务中)\r\n在堆栈跟踪的RaisersEdge.Controllers.BaseController.d__6.MoveNext()\r\n---末端,从抛出异常的以前位置开始 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在堆栈跟踪的System.Threading.Tasks.TaskHelpersExtensions.d__3`1.MoveNext()\r\n---末端,从抛出异常的以前位置开始 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在堆栈跟踪的System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n---末端,从抛出异常的以前位置开始 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在堆栈跟踪的System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n---末端,从抛出异常的以前位置开始 \r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task TargetSite:"System.Net.Http.HttpResponseMessage EnsureSuccessStatusCode()",_typeTag:"HttpRequestException"}
发布于 2019-07-08 05:13:27
正如其他人提到的,应该而不是.Result,因为它是恶毒!但是,在您使用遗留应用程序的情况下,您可以使用以下解决方法:
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类创建、配置和运行一个异步任务,然后打开它并同步等待它以获得结果:这几乎是等待所做的,这种方法将防止死锁,可以在try/catch块中使用。
当然,调用async方法的唯一正确方法是使用await,但是当您无法在同步方法中调用async方法时,这个解决方案是一个更好的选择。
发布于 2019-07-10 04:28:06
运行异步是如何导致死锁的?
方法在这种情况下使用。如果这个上下文一次只允许一个线程,并且调用代码通过调用Result或Wait在该上下文中阻塞一个线程,那么方法无法恢复。 (因此无法完成)。
如何添加ConfigureAwait(假)来解决这个问题?
因为await不再捕获它的上下文。async方法可以在任何线程池线程上恢复,并且不受上下文中阻塞的线程的影响。通常被认为是库代码的最佳实践。。
其中一个服务位于遗留环境中,无法调用等待,因为我没有要覆盖的异步函数。
有一个可用于尝试安全阻止异步代码的各种黑客。。他们中没有人在每一种情况下都能工作。如果ConfigureAwait(false)对你有用,我就会用它。
https://stackoverflow.com/questions/56928102
复制相似问题