投票方式正确
本文关键字:方式正 | 更新日期: 2023-09-27 18:00:35
我是一名软件/硬件工程师,在C和嵌入式技术方面有着丰富的经验。目前,我正忙于用C#(.NET)编写一些使用硬件进行数据采集的应用程序。现在下面,对于我燃烧,问题:
例如:我有一台机器,它有一个末端开关,用于检测轴的最终位置。现在我使用USB数据采集模块来读取数据。目前我正在使用线程来连续读取端口状态。
此设备上没有中断功能。
我的问题是:这是正确的方式吗?我应该使用定时器、线程还是任务?我知道投票是你们大多数人"讨厌"的事情,但任何建议都是受欢迎的!
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);
在大多数情况下,您可以只使用Task.Run(() => DoWork(), token)
,但没有过载来提供TaskCreationOptions.LongRunning
选项,该选项"告诉"任务调度程序不要使用普通的线程线程
但正如您所看到的,Tasks
更容易处理(并且await
可以,但不适用于此处)。特别是在这个实现中,"停止"只是从代码中的任何位置调用cancellationTokenSource.Cancel()
。
您甚至可以在多个操作中共享此令牌,并立即停止它们。此外,当令牌被取消时,尚未启动的任务也不会启动。
您还可以将另一个操作附加到任务上,以便在一个任务之后运行:
listener.ContinueWith(t => ShutDown(t));
然后在侦听器完成后执行此操作,您可以进行清理(如果未成功,t.Exception
包含tasks操作的异常)。
IMO轮询无法避免。
您可以创建一个模块,该模块具有独立的线程/Task,它将定期轮询端口。根据数据的变化,此模块将引发将由消费应用程序处理的事件
可能是:
public async Task Poll(Func<bool> condition, TimeSpan timeout, string message = null)
{
// https://github.com/dotnet/corefx/blob/3b24c535852d19274362ad3dbc75e932b7d41766/src/Common/src/CoreLib/System/Threading/ReaderWriterLockSlim.cs#L233
var timeoutTracker = new TimeoutTracker(timeout);
while (!condition())
{
await Task.Yield();
if (timeoutTracker.IsExpired)
{
if (message != null) throw new TimeoutException(message);
else throw new TimeoutException();
}
}
}
查看SpinWait或Task。延迟内部。
我一直在考虑这个问题,您可能会在利用Tasks和Func、Action的基础上构建一个抽象层,轮询服务将Func、Action和轮询间隔作为args。这将使这两种功能的实现保持独立,同时将它们开放以注入到轮询服务中。
例如,你会有这样的东西作为你的投票类
public class PollingService {
public void Poll(Func<bool> func, int interval, string exceptionMessage) {
while(func.Invoke()){
Task.Delay(interval)
}
throw new PollingException(exceptionMessage)
}
public void Poll(Func<bool, T> func, T arg, int interval, string exceptionMessage)
{
while(func.Invoke(arg)){
Task.Delay(interval)
}
throw new PollingException(exceptionMessage)
}
}