轮询正确的方法? [英] Polling the right way?
问题描述
我是一位软件/硬件工程师,在C和嵌入式技术方面拥有相当丰富的经验.目前,我正在忙于使用硬件进行数据采集的C#(.NET)编写一些应用程序.现在,以下内容对我来说很棘手:
I am a software/hardware engineer with quite some experience in C and embedded technologies. Currently i am busy with writing some applications in C# (.NET) that is using hardware for data acquisition. Now the following, for me burning, question:
例如:我有一台机器,该机器带有用于检测轴的最终位置的末端开关.现在,我正在使用USB数据采集模块读取数据.目前,我正在使用线程来连续读取端口状态.
For example: I have a machine that has an endswitch for detecting the final position of an axis. Now i am using a USB Data acquisition module to read the data. Currently I am using a Thread to continuously read the port-status.
此设备上没有中断功能.
There is no interrupt functionality on this device.
我的问题:这是正确的方法吗?我应该使用计时器,线程还是任务?我知道大多数人都讨厌投票,但是欢迎任何建议!
My question: Is this the right way? Should i use timers, threads or Tasks? I know polling is something that most of you guys "hate", but any suggestion is welcome!
推荐答案
IMO,这在很大程度上取决于您的实际环境,但是首先-在大多数情况下,您不应再使用线程. Tasks
越方便,越方便强大的解决方案.
IMO, this heavily depends on your exact environment, but first off - You should not use Threads anymore in most cases. Tasks
are the more convenient and more powerful solution for that.
-
低轮询频率:
Tick
事件中的计时器+轮询:
计时器易于操作和停止.无需担心线程/任务在后台运行,但是处理发生在主线程中
Low polling frequency: Timer + polling in the
Tick
event:
A timer is easy to handle and stop. No need to worry about threads/tasks running in the background, but the handling happens in the main thread
中轮询频率:Task
+ await Task.Delay(delay)
:
await Task.Delay(delay)
不会阻塞线程池线程,但是由于上下文切换,最小延迟为〜15ms
Medium polling frequency: Task
+ await Task.Delay(delay)
:
await Task.Delay(delay)
does not block a thread-pool thread, but because of the context switching the minimum delay is ~15ms
高轮询频率:Task
+ Thread.Sleep(delay)
可用延迟为1毫秒-实际上,我们这样做是为了轮询我们的USB测量设备
High polling frequency: Task
+ Thread.Sleep(delay)
usable at 1ms delays - we actually do this to poll our USB measurement device
这可以通过以下方式实现:
This could be implemented as follows:
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);
在大多数情况下,您只能使用Task.Run(() => DoWork(), token)
,但是没有提供TaskCreationOptions.LongRunning
选项的重载,它告诉任务计划程序不要使用普通的线程池线程.
但是如您所见,Tasks
更易于处理(并且await
可用,但不适用于此处).特别是在此实现中,停止"只是从代码中的任何位置调用cancellationTokenSource.Cancel()
.
In most cases you can just use Task.Run(() => DoWork(), token)
, but there is no overload to supply the TaskCreationOptions.LongRunning
option which tells the task-scheduler to not use a normal thread-pool thread.
But as you see Tasks
are easier to handle (and await
able, but does not apply here). Especially the "stopping" is just calling cancellationTokenSource.Cancel()
in this implementation from anywhere in the code.
您甚至可以在多个操作中共享此令牌,并立即停止它们.此外,取消令牌后,尚未启动的任务也不会启动.
You can even share this token in multiple actions and stop them at once. Also, not yet started tasks are not started when the token is cancelled.
您还可以将另一个操作附加到一个任务上,以在一个任务之后运行:
You can also attach another action to a task to run after one task:
listener.ContinueWith(t => ShutDown(t));
然后在侦听器完成之后执行此操作,您可以执行清理操作(t.Exception
如果未成功执行,则包含task操作的异常).
This is then executed after the listener completes and you can do cleanup (t.Exception
contains the exception of the tasks action if it was not successful).
这篇关于轮询正确的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!