源码:
public static SOAResponse<DetectFaceResponse> DetectFace(string imgUrl) { var soaResponse = new SOAResponse<DetectFaceResponse> { Code = ErrorCode.对象不存在 }; try { var detectFaceRequest = new DetectFaceRequest { Url = imgUrl }; soaResponse.Body = CreateIaiClient().DetectFace(detectFaceRequest). ConfigureAwait(false).GetAwaiter().GetResult(); soaResponse.Code = ErrorCode.成功; } catch (Exception ex) { soaResponse.DefaultMessage = ex.Message; if (soaResponse.DefaultMessage.Contains("FailedOperation.ImageFacedetectFailed")) { soaResponse.AddDefaultMessage("上传头像不存在人脸"); } } return soaResponse; }
犯的错误: 异步编程忌讳同步方法调取异步方法,最大的注意点静态方法调取异步方法。
根据微软介绍ConfigureAwait(false)不会产生死锁了,这意味着通知异步状态机 AsyncMethodStateMachine 并不需要使用设置好的
SynchronizationContext(对于 UI 线程,是 DispatcherSynchronizationContext)执行线程同步,
而是使用默认的 SynchronizationContext,而默认行为是随便找个线程执行后面的代码。但是这个方法并没有生效
最后的解决方法是
1:全部改成异步编程
2:重写上下文请求,改成同步上下文
第一种改动量过大放弃
第二种从网上找了一个帮助类
public static class AsyncHelpers { /// <summary> /// Execute's an async Task<T> method which has a void return value synchronously /// </summary> /// <param name="task">Task<T> method to execute</param> //public static void RunSync(Func<Task> task) //{ // var oldContext = SynchronizationContext.Current; // var synch = new ExclusiveSynchronizationContext(); // SynchronizationContext.SetSynchronizationContext(synch); // synch.Post(async _ => // { // try // { // await task(); // } // catch (Exception e) // { // synch.InnerException = e; // throw; // } // finally // { // synch.EndMessageLoop(); // } // }, null); // synch.BeginMessageLoop(); // SynchronizationContext.SetSynchronizationContext(oldContext); //} /// <summary> /// Execute's an async Task<T> method which has a T return type synchronously /// </summary> /// <typeparam name="T">Return Type</typeparam> /// <param name="task">Task<T> method to execute</param> /// <returns></returns> public static T RunSync<T>(Func<Task<T>> task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); T ret = default(T); synch.Post(async _ => { try { ret = await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); return ret; } //internal static T RunSync<T>(Task<T> task) //{ // throw new NotImplementedException(); //} private class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false); readonly Queue<Tuple<SendOrPostCallback, object>> items = new Queue<Tuple<SendOrPostCallback, object>>(); public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("We cannot send to our same thread"); } public override void Post(SendOrPostCallback d, object state) { lock (items) { items.Enqueue(Tuple.Create(d, state)); } workItemsWaiting.Set(); } public void EndMessageLoop() { Post(_ => done = true, null); } public void BeginMessageLoop() { while (!done) { Tuple<SendOrPostCallback, object> task = null; lock (items) { if (items.Count > 0) { task = items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (InnerException != null) // the method threw an exeption { throw new AggregateException(InnerException.Message, InnerException); } } else { workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } }
问题解决。
最后总结:异步编程避免在同步方法中调取异步方法,自己写的类库可能不会出现死锁现象,但是如果是通过http请求第三方接口的话,极其容易死锁