【发布时间】:2023-04-08 21:55:01
【问题描述】:
我正在使用 C# 来控制硬件设备。该程序的结构为
-
硬件控制线程(正常 CPU 优先级)
while (notFinished) { Prepare(); await DeviceCommunication(); autoResetEvent.WaitOne(); } 一个 UI 线程(正常 CPU 优先级)
- 繁重的计算线程(低于正常 CPU 优先级)
有一层用C#写的设备APITask。 Task 延续后的 AutoResetEvent 延迟有时高达 500 毫秒,具体取决于 PC 的状态(繁重的计算线程甚至没有运行)。除了在一些关键的硬件控制时刻外,它通常都很好。它需要 10 毫秒的响应时间。
我测试了将消费者线程设置为高于正常值并模拟异步函数以强制它同步。它似乎解决了这个问题。但是,在真正的异步函数中,有await。他们立即释放线程。延续在线程池中的线程中。
问题一
500ms延迟正常吗?我正在使用带有 i5 2 线程的 VirtualBox。我希望目标 PC 的性能与我的相似。
假设我的发现是有效的。为了解决这个问题,我的选择是
- 使用
Task.GetAwaiter().GetResult()将异步转为同步。它不应导致死锁。 - 重写设备 API 层以支持真正的同步操作。它很优雅并且遵循一般规则,但它们很好。
- 设置
Task调度优先级和CPU优先级 - 使用第 3 方
Task库
问题 2
有更好的选择吗?
问题 3
选项3怎么做(设置Task调度优先级和CPU优先级)?自定义TaskScheduler 是唯一的方法吗?
【问题讨论】:
-
这是从 UI 线程调用的吗?如果是这样,如果您尝试改用
await DeviceCommunication().ConfigureAwait(false);会发生什么? -
Re Q1 - 500ms 远远超出了等待状态机通常引入的原始开销,在普通现代笔记本电脑上,它更多地处于(非常粗略)50uS(即快约 10000 倍)的范围内。不过,50uS 确实与大约 0uS 的同步时间相比,因此异步不是高性能非 I/O 任务的自然选择。但是,如果您要达到 10 毫秒的目标,我怀疑本机 async-await 开销是阻碍的原因。它会是别的东西。这里的 UI 是什么(控制台/Winforms/WPF)?
-
它不会在 UI 线程中继续。延续在线程池的线程上。实际上,有一个分离 UI 的 WCF 层。我已经更正了我的问题的描述。对困惑感到抱歉。等待状态机开销不应该是问题。问题应该是 CPU 调度。
-
谁以及何时负责设置 autoResetEvent?
-
请注意,同步
WaitOne()调用将阻塞线程池线程(如果可能,您通常希望避免这种情况)。我建议看看Stephen Cleary's Nito library;有一个可用的 asyncAutoResetEvent版本以及许多其他有用的异步原语。
标签: c# windows multithreading task real-time