首页 > 解决方案 > 是否可以在不等待任务的情况下异步捕获异常?

问题描述

我有一个低级 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 退出的线程上创建的错误报告事件和进度报告的变体。这些不能按预期工作/或我不明白他们在做什么。

标签: c#asynchronousexception-handlingasync-awaitproducer-consumer

解决方案


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

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

  • 捕获低级设备抛出的任何异常并将它们传播到高级设备(见下文),它将错误存储在一个属性Error中。
  • 将 HL 设备的所有读取封装在属性中,以便在读取时可以检查Error. 如果发生错误,则抛出带有详细信息的异常(将被捕获并在 中处理Main。)

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

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

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

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

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


推荐阅读