轮询正确的方式?

Vel*_*ity 19 c# hardware multithreading task polling

我是一名软件/硬件工程师,在C和嵌入式技术方面拥有丰富的经验.目前我正在忙着用C#(.NET)编写一些使用硬件进行数据采集的应用程序.现在以下,对我来说,燃烧,问题:

例如:我有一台机器,它有一个用于检测轴最终位置的开关.现在我正在使用USB数据采集模块来读取数据.目前我正在使用Thread来连续读取端口状态.

此设备上没有中断功能.

我的问题:这是正确的方法吗?我应该使用计时器,线程还是任务?我知道民意调查是你们大多数人"讨厌"的事情,但欢迎任何建议!

Chr*_*Fin 42

IMO,这在很大程度上取决于您的确切环境,但首先关闭 - 在大多数情况下,您不应再使用Threads.Tasks是更方便,更强大的解决方案.

  • 低轮询频率:Tick事件中的定时器+轮询:
    定时器易于处理和停止.无需担心后台运行的线程/任务,但处理发生在主线程中

  • 中等轮询频率:Task+ await Task.Delay(delay):
    await Task.Delay(delay)不阻塞线程池线程,但由于上下文切换,最小延迟为~15ms

  • 高轮询频率:Task+ Thread.Sleep(delay)
    在1ms延迟时间内可用 - 我们实际上这是为了轮询我们的USB测量设备

这可以实现如下:

int delay = 1;
var cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
var listener = Task.Factory.StartNew(() =>
{
    while (true)
    {
        // poll hardware

        Thread.Sleep(delay);
        if (token.IsCancellationRequested)
            break;
    }

    // cleanup, e.g. close connection
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,你可以使用Task.Run(() => DoWork(), token),但没有重载提供TaskCreationOptions.LongRunning选项,告诉任务调度程序不使用普通的线程池线程.
但是你看到Tasks的更容易处理(并且await能够,但不适用于此).特别是"停止"只是cancellationTokenSource.Cancel()从代码中的任何地方调用此实现.

您甚至可以在多个操作中共享此令牌并立即停止它们.此外,取消令牌时,尚未启动尚未启动的任务.

您还可以将另一个操作附加到任务以在一个任务之后运行:

listener.ContinueWith(t => ShutDown(t));
Run Code Online (Sandbox Code Playgroud)

然后在侦听器完成后执行此操作,您可以执行清理(t.Exception如果未成功,则包含任务操作的例外).