c# - 是否可以在不等待任务的情况下异步捕获异常?
问题描述
我有一个低级 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 退出的线程上创建的错误报告事件和进度报告的变体。这些不能按预期工作/或我不明白他们在做什么。
解决方案
当您想异步启动一个方法并在稍后与它同步以获得结果时,您的标题问题适用。但是,您问题的主体所描述的实际上是对共享状态(高级设备)的并发访问。该状态是从您的Main
线程中读取的,但由您的低级设备在后台线程上写入。
Error
解决方案是在高级设备中创建一个属性,该属性可用于协调两个线程之间的错误处理:
- 捕获低级设备抛出的任何异常并将它们传播到高级设备(见下文),它将错误存储在一个属性
Error
中。 - 将 HL 设备的所有读取封装在属性中,以便在读取时可以检查
Error
. 如果发生错误,则抛出带有详细信息的异常(将被捕获并在 中处理Main
。)
最终结果是来自低级设备的异常已传播到Main
.
作为旁注,您的问题暗示了基于任务的异步模式,但您的低级设备实际上是以基于事件的方式编写的。请参阅异步编程模式。
每种模式都有特定的传播错误的方法:
- 对于基于事件的模式(EAP),您可以通过您引发的任何事件的事件参数传播错误。请参阅实现基于事件的异步模式的最佳实践。
- 对于基于任务的模式(TAP),当您
await
执行Task
您Task.Run
实际上仅具有将低级设备循环放在不同线程上的效果,Main
您不能等待readerTask
,因为它代表了整个处理循环,而不是单个数据包更新。各个数据包更新是通过事件通知的。
总而言之,当低级设备捕获异常时,它应该引发一个事件并在事件的事件参数中传递异常的详细信息。高级设备将接收事件并将详细信息存储在其Error
属性中。这一切都发生在您的后台线程上。在主线程上,当在高级设备上读取属性期间检测到错误时,getter 应该抛出一个异常,其中包含Error
要处理的详细信息Main
。
推荐阅读
- typescript - TypeScript 动态联合
- r - 连接列名和R中的第一行
- python - 使用 Tkinter 在框架内放置框架时遇到困难
- r - 在读取 R 中多个经过验证的 json 文件的 jsonlite 中解析错误
- javascript - Joi - 多个 `when` 子句
- sql - 根据存储为 Char(4) 的时间计算持续时间
- html - 对齐 CSS 网格布局卡上的元素
- acumatica - Acumatica 错误:错误:系统未能提交 MapFrom 行
- forms - Grav CMS:如何从模板访问表单标签(复选框)
- azure - 如何在 Kubernetes (AKS) 中使用 Helm 注释 Ingress Controller 的 pod?