首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HttpClient SendAsync DeadLock

HttpClient SendAsync DeadLock
EN

Stack Overflow用户
提问于 2019-07-08 03:33:25
回答 2查看 1.9K关注 0票数 0

我有一个包装器,它创建了一个函数--公共异步任务getCacheToken --为一些内部服务/应用程序调用

通过在另一个服务中调用performExtract(),我正在经历这个异常(请参见下面的内容)。

performExtract实际上是通过API调用调用getCacheToken。

我无法帮助在同步方法(遗留环境)内发送异步调用,所以每次在循环中调用var results = client.SendAsync(requestData).Result‘时,都会导致死锁,如果我正确理解它,for循环中的sendAsync将在启动另一个任务之前等待任务完成,因此它不应该有以下异常(处理连接?)

要修复它,我必须重写发送异步ConfigureAwait(false),它解决了我的问题。

我的问题是添加ConfigureAwait(false)如何解决这个问题?

为了避免这个问题,您可以使用一个名为ConfigureAwait的方法,其中包含一个false参数。当您这样做时,这会告诉Task,它可以在任何可用的线程上恢复自身,而不是等待最初创建它的线程。这将加快响应速度,避免许多死锁。

运行异步是如何导致死锁的?

非常感谢你所有的耐心阅读通过这篇文章。

代码语言:javascript
复制
 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"}

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-07-08 05:13:27

正如其他人提到的,应该而不是.Result,因为它是恶毒!但是,在您使用遗留应用程序的情况下,您可以使用以下解决方法:

代码语言:javascript
复制
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();
        }
}

然后,您可以使用助手轻松地调用您的方法:

代码语言:javascript
复制
var results = AsyncHelper.RunSync<System.Net.Http.HttpResponseMessage>( 
 () => client.SendAsync(requestData)
);

helper类创建、配置和运行一个异步任务,然后打开它并同步等待它以获得结果:这几乎是等待所做的,这种方法将防止死锁,可以在try/catch块中使用。

当然,调用async方法的唯一正确方法是使用await,但是当您无法在同步方法中调用async方法时,这个解决方案是一个更好的选择。

票数 2
EN

Stack Overflow用户

发布于 2019-07-10 04:28:06

运行异步是如何导致死锁的?

方法在这种情况下使用。如果这个上下文一次只允许一个线程,并且调用代码通过调用ResultWait在该上下文中阻塞一个线程,那么方法无法恢复。 (因此无法完成)。

如何添加ConfigureAwait(假)来解决这个问题?

因为await不再捕获它的上下文。async方法可以在任何线程池线程上恢复,并且不受上下文中阻塞的线程的影响。通常被认为是库代码的最佳实践。

其中一个服务位于遗留环境中,无法调用等待,因为我没有要覆盖的异步函数。

有一个可用于尝试安全阻止异步代码的各种黑客。。他们中没有人在每一种情况下都能工作。如果ConfigureAwait(false)对你有用,我就会用它。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56928102

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档