代码之家  ›  专栏  ›  技术社区  ›  Lukasz Madon

捕获同步调用的异步代码中的异常

  •  1
  • Lukasz Madon  · 技术社区  · 10 年前

    我有储蓄服务进行身份验证。 catch (AccountNotFoundException) 除非我在Task.Run中调用它,否则不会捕捉到异常。奇怪的是测试用例很好。为什么?是因为task.start()与catch处于不同的级别吗?

        public override string GetUserNameByEmail(string email)
        {
             var task = client.GetUserByEmail(email, false);
             return task.Result;
             // I changed to
             // return Task.Run(() => client.GetUserByEmail(email, false)).Result.UserName;
             // and I was able to catch the exception
        }
    
        public async Task<AccountDetails> GetAccountDetailsByEmail(string email)
        {
            try
            {
                return await Call(() => client.getAccountDetailsByEmail(email));
            }
            catch (AccountNotFoundException)
            {
                return null;
            }
        }
    
        private async Task<T> Call<T>(Func<T> call)
        {
            try
            {
                transport.Open();
                var thriftTask = new Task<T>(call);
                thriftTask.Start();
                return await thriftTask;
            }
            catch (DatabaseException e)
            {
                Logger.Error(e);
                throw;
            }
            finally
            {
                transport.Close();
            }
        }
    

    测试用例工作正常

        [TestMethod]
        public async Task Nonexisting_User_I_Expect_To_Be_Null()
        {
            var user = Provider.GetUser("idontexist@bar.com", false);
            Assert.IsNull(user);
        }
    

    编辑:

    我有一个理论解释为什么我的代码运行正常:代码之所以有效是因为我很幸运。请求和异步由同一线程处理,因此它共享相同的上下文,因此不会阻塞。

    2 回复  |  直到 10 年前
        1
  •  3
  •   Stephen Cleary    10 年前

    首先,你 不应该 正在同步调用异步方法。正如我在博客上所描述的 approach you're using is prone to deadlocks .

    您看到意外异常类型的原因是 Result 将在 AggregateException 。为了避免这种情况,您可以拨打 GetAwaiter().GetResult() .

    这和你无关 Start 但既然你提到了 开始 成员实际上没有用例。从来没有好的理由使用它。相反,使用 Task.Run :

    var thriftTask = Task.Run(call);
    
        2
  •  1
  •   Colin    10 年前

    有关异步代码异常处理的详细信息,请参阅此处。可能是您正在捕获AccountNotFoundException,而您确实希望捕获异常,该异常将具有 内部异常 设置为AccountNotFoundException:

    https://msdn.microsoft.com/en-us/library/0yd65esw.aspx

    摘录:

    任务的IsFaulted属性设置为True,任务的Exception.InnerException属性设置为异常,异常在catch块中捕获。

        public async Task DoSomethingAsync()
        {
            Task<string> theTask = DelayAsync();
    
            try
            {
                string result = await theTask;
                Debug.WriteLine("Result: " + result);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception Message: " + ex.Message);
            }
            Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
            Debug.WriteLine("Task IsFaulted:  " + theTask.IsFaulted);
            if (theTask.Exception != null)
            {
                Debug.WriteLine("Task Exception Message: "
                    + theTask.Exception.Message);
                Debug.WriteLine("Task Inner Exception Message: "
                    + theTask.Exception.InnerException.Message);
            }
        }
    
        private async Task<string> DelayAsync()
        {
            await Task.Delay(100);
    
            // Uncomment each of the following lines to 
            // demonstrate exception handling. 
    
            //throw new OperationCanceledException("canceled");
            //throw new Exception("Something happened.");
            return "Done";
        }