是否有可能在不等待任务的情况下异步捕获异常?

时间:2018-10-17 15:13:23

标签: c# asynchronous exception-handling async-await producer-consumer

我有一个低级CAN设备类,我想为其创建onMessageReceive事件。我有几个可以使用此CAN类实例的高级设备类。我想将高级设备类的消息解析器附加到低级CAN设备的onMessageReceive事件。这样,当低级类收到数据包时,低级读取器任务会将其解析为高级类。放入代码,如下所示。

void Main()
    {
        try
        {
            using (HighLevelDevice highLevelDevice = new HighLevelDevice())
            {
                while (true)
                {
                    // Use the properties/fields in highLevelDevice to make testing decisions.
                }
            }
        }
        catch (Exception)
        {
            // If the low level CAN reader task encounters an error I would like for it to asynchronously propogate up to here.
            throw;
        }
    }


    public class HighLevelDevice
    {
        private LowLevelCan lowLevelCanInstance;

        public HighLevelDevice()
        {
            lowLevelCanInstance = new LowLevelCan(this.ProcessPacket);
        }

        private void ProcessPacket(Packet packet)
        {
            // Convert packet contents into high level device properties/fields.
        }
    }

    public class LowLevelCan
    {
        private delegate void ProcessPacketDelegate(Packet packet);

        private ProcessPacketDelegate processPacket;

        private Task readerTask;

        public LowLevelCan(Action<Packet> processPacketFunction)
        {
            processPacket = new ProcessPacketDelegate(processPacketFunction);
            readerTask = Task.Run(() => readerMethod());
        }

        private async Task readerMethod()
        {
            while(notCancelled) // This would be a cancellation token, but I left that out for simplicity.
            {
                processPacket(await Task.Run(() => getNextPacket()));
            }
        }

        private Packet getNextPacket()
        {
            // Wait for next packet and then return it.
            return new Packet();
        }
    }

    public class Packet
    {
        // Data packet fields would go here.
    }

如果在getNextPacket中引发异常,我希望将其捕获在main中。这有可能吗?如果我不满意并完全误解了异步,我深表歉意。如果可能发生这种情况,我该如何改变实现的方法?我可以定期检查读者的状态,但如果可能的话,我想避免这种情况。

此实现将杀死阅读器,但highLevelDevice线程会明显继续运行。如果我存储了错误并偶尔在主线程上检查了状态,那就可以了。我只想找到一种避免这种情况的解决方案。

我尝试了在highLevelDevice退出的线程上创建的错误报告事件和进度报告的变体。这些无法正常工作/或者我不知道它们在做什么。

2 个答案:

答案 0 :(得分:0)

当您要异步启动方法并在以后与它同步以获取结果时,您的标题问题适用。但是,您的问题主体所描述的实际上是对共享状态(高级设备)的并发访问。状态是从Main线程中读取的,但是由您的低级设备写入后台线程中。

解决方案是在高级设备中创建Error属性,该属性可用于协调两个线程之间的错误处理:

  • 捕获您的低级设备引发的所有异常,并将其传播到高级设备(请参见下文),该异常会将错误存储在属性Error中。
  • 将HL设备的所有读取封装在属性中,以便在读取时可以检查Error。如果发生错误,请抛出一个异常,并提供详细信息(在Main中进行捕获和处理。)

最终结果是,来自低级设备的异常已传播到Main

作为旁注,您的问题暗示着基于任务的异步模式,但您的低级设备实际上是以基于事件的方式编写的。参见Asynchronous programming patterns

每种模式都有传播错误的特定方法:

您的Task.Run实际上仅具有将低级设备循环放在Main的不同线程上的作用,您无法等待readerTask,因为它将处理循环表示为整体更新,而不是单个数据包更新。而是通过事件通知各个数据包更新。

总而言之,当低级设备捕获到异常时,它应该引发一个事件并在事件的事件args中传递该异常的详细信息。高级设备将接收事件并将详细信息存储在其Error属性中。这一切都发生在您的后台线程上。在主线程上,当在高级设备上读取属性时检测到错误时,getter应引发一个异常,其中包含Error详细信息,并将在Main中进行处理。

答案 1 :(得分:-1)

不。我已经对此进行了测试,您必须等待任务完成才能引发异常。等待任务或使用Task.Wait()等待任务完成。

我尝试使用此代码,但未捕获到异常。

        try
        {
            var task = Task.Run(() => WaitASecond());

            task.ContinueWith(failedTask => throw failedTask.Exception, TaskContinuationOptions.OnlyOnFaulted);
        }
        catch (Exception ex)
        {
            throw;
        }

当我在task.Wait()下添加var task = Task.Run(() => WaitASecond());时,它捕获到一个汇总异常并将其抛出。

您必须等待所有任务完成才能捕获异常并将其扔到Main()。