简体   繁体   English

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

[英]Catching exceptions in async code called synchronously

I have thrift service for authentication. 我有节俭的身份验证服务。 catch (AccountNotFoundException) doesn't catch the exception unless I call it in Task.Run. catch (AccountNotFoundException)不会捕获异常,除非我在Task.Run中调用它。 The strange thing is that test case is fine. 奇怪的是,测试用例很好。 Why? 为什么? Is it because task.start() is on the different level than catch? 是因为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();
        }
    }

Test case works just fine 测试用例工作正常

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

EDIT: 编辑:

I have a following theory why my code run ok: The code was working because I was lucky. 我有以下理论为什么我的代码可以正常运行:代码运行正常是因为我很幸运。 Request and async was handled by the same thread so it shared the same context so it didn't block. 请求和异步由同一线程处理,因此它共享相同的上下文,因此不会阻塞。

First, you shouldn't be calling asynchronous methods synchronously. 首先,您应该同步调用异步方法。 As I describe on my blog, the approach you're using is prone to deadlocks . 正如我在博客上所描述的那样, 您使用的方法容易出现死锁

The reason you're seeing an unexpected exception type is because Result will wrap any task exceptions in an AggregateException . 您看到意外的异常类型的原因是因为Result将所有任务异常包装在AggregateException To avoid this, you can call GetAwaiter().GetResult() . 为了避免这种情况,可以调用GetAwaiter().GetResult()

This doesn't have anything to do with Start , but since you mention it, the Start member doesn't really have a use case. 这与Start无关,但是自从您提到它以来, Start成员实际上并没有用例。 There's never a good reason to use it. 从来没有充分的理由使用它。 Instead, use Task.Run : 而是使用Task.Run

var thriftTask = Task.Run(call);

See here for details of exception handling for async code. 请参阅此处以获取异步代码异常处理的详细信息。 It may be that you're catching an AccountNotFoundException, when you really want to be catching an Exception, which will have an InnerException set to the AccountNotFoundException: 当您确实想捕获一个Exception时,可能是您正在捕获AccountNotFoundException,该异常会将InnerException设置为AccountNotFoundException:

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

An excerpt: 摘录:

The task's IsFaulted property is set to True, the task's Exception.InnerException property is set to the exception, and the exception is caught in the catch block. 任务的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";
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM