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

时间:2022-05-01 21:01:43

I have thrift service for authentication. catch (AccountNotFoundException) doesn't catch the exception unless I call it in Task.Run. The strange thing is that test case is fine. Why? Is it because task.start() is on the different level than catch?

我有节俭服务进行身份验证。 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();
        }
    }

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.

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

2 个解决方案

#1


3  

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. To avoid this, you can call GetAwaiter().GetResult().

您看到意外异常类型的原因是Result将在AggregateException中包装任何任务异常。为避免这种情况,您可以调用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. There's never a good reason to use it. Instead, use Task.Run:

这与Start没有任何关系,但是因为你提到它,Start成员实际上没有用例。使用它从来没有充分的理由。相反,使用Task.Run:

var thriftTask = Task.Run(call);

#2


1  

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/en-us/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";
    }

#1


3  

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. To avoid this, you can call GetAwaiter().GetResult().

您看到意外异常类型的原因是Result将在AggregateException中包装任何任务异常。为避免这种情况,您可以调用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. There's never a good reason to use it. Instead, use Task.Run:

这与Start没有任何关系,但是因为你提到它,Start成员实际上没有用例。使用它从来没有充分的理由。相反,使用Task.Run:

var thriftTask = Task.Run(call);

#2


1  

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/en-us/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";
    }

相关文章