在 C# 中异步等待继续后的实时 AutoResetEvent

Posted

技术标签:

【中文标题】在 C# 中异步等待继续后的实时 AutoResetEvent【英文标题】:Real time AutoResetEvent after async-await continuation in C# 【发布时间】:2019-04-27 06:40:09 【问题描述】:

我正在使用 C# 来控制硬件设备。该程序的结构为

    硬件控制线程(正常 CPU 优先级)

    while (notFinished)
    
        Prepare();
        await DeviceCommunication();
        autoResetEvent.WaitOne();
    
    

    一个 UI 线程(正常 CPU 优先级)

    繁重的计算线程(低于正常 CPU 优先级)

有一层用C#写的设备APITaskTask 延续后的 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;有一个可用的 async AutoResetEvent 版本以及许多其他有用的异步原语。 【参考方案1】:

Windows 不提供任何类型的调度保证。时期。当设备驱动程序繁忙时,我们经常会在 Win7 系统上看到 C# 代码的额外延迟数秒。如果您确实有严格的实时要求,则需要在 RTOS 上运行。

【讨论】:

另一方面,游戏、电影和歌曲都可以正常播放。他们通过使用计划优先级和multimedia class scheduler 来做到这一点。几秒钟的延迟是巨大的。如果您看到几秒钟的延迟,则不是操作系统有问题。 RTOS 并不意味着没有延迟,它意味着甚至拒绝启动无法在其约束范围内完成的任务。这也需要专门的编程。 @Panagiotis:“电影和歌曲播放得很好” -- 大多数时间。有很多方法可以导致视频和音频播放卡顿/碎片/等。在 Windows 或任何其他主流操作系统上。甚至多媒体 API 也有限制。 我知道。我什至记得它是一个 16 位进程,以确保它得到有保证的调度。 RTOS 与这个问题关系不大。我还要重复一遍,如果你有第二长的延误,你就会有糟糕的司机问题。 特别是当您与专门的设备供应商合作时 - 编写好的设备驱动程序的压力不存在。他们没有从他们身上赚钱,而且他们的数量也不是很大,如果司机慢,他们会被羞辱出市场。 事实上,我记得花了 几个月 才让 FTDI 相信它的以太网串行驱动程序没有正确处理握手。 他们的样本,使用手动握手,工作。我不得不指出他们显然无法与他们的驱动程序一起使用的 MSDN 文档示例,即使它适用于其他所有人的设备。

以上是关于在 C# 中异步等待继续后的实时 AutoResetEvent的主要内容,如果未能解决你的问题,请参考以下文章

C#如何使用异步编程

C#如何在不等待完成的情况下启动异步方法?

C# Winform 多线程异步委托进度条

C#及Java异步编程介绍

C#异步编程之async/await

节点在继续之前等待异步功能