在最近做的一个项目上,上位机需要定时的发送采集指令到硬件,采集的频率为5ms。采用了线程池多线程处理数据,但是使用定时器Timer空间和Thread.sleep(5)都没能实现。出现了在部分时间点上能够达到要求,部分点的间隔达到了15.6毫秒,远达不到技术要求。为了解决这一问题,查询了大量的资料。怀疑这个与windows系统的cpu时间分配方式和线程沉睡后的唤醒有关。简单的讲一下,windows采用的是抢占式的时间分配,系统会根据每个线程的优先等级和线程运行时间计算出下一个运行的线程。当线程t1被唤醒时,不会立即执行而是先等当前在执行的线程tc的时间片结束后再和所有的线程一起计算出cpu下一个时间片执行的线程。这应该就是导致为什么会出现部分时间点间隔为5毫秒,部分时间间隔远大于5毫秒。
解决这个问题,采用了Thread.sleep(0)这个方法,其意思是重新计算线程的优先等级。在代码中多处添加该语句使程序cpu能够经常访问时间对比语句。
代码如下,两种方法都可以实现
在方法二中做了改进,加入了一个沉睡三秒语句,其中用是避免时间比较大量的占用线程时间。
还有可以通过调用windows的API实现。
方法一:
[DllImport("winmm")]
static extern uint timeGetTime();
[DllImport("winmm")]
static extern void timeBeginPeriod(int t);
[DllImport("winmm")]
static extern uint timeEndPeriod(int t);
private void timer() //调用API
{
uint timerstart = timeGetTime();
while (true)
{
uint i = 0;
while (i < 5) //N为时间间隔(ms)
{
i = timeGetTime() - timerstart;
}
timerstart = timeGetTime();
sendMsg(); //需要循环运行的函数;
}
}
方法二:
private void timer_() //使用DateTime类
{
long lt1 = DateTime.Now.ToFileTime();//取当前时间
while (true)
{
long lt = 0;
while (lt < 50000) //N为时间间隔(0.1us)
{
long lt2 = DateTime.Now.ToFileTime();
lt = lt2 - lt1;
if (lt < 100)
{
Thread.Sleep(3);
}
}
//Console.WriteLine(lt);
lt1 = DateTime.Now.ToFileTime();
sendMsg(); //需要循环运行的函数;
}
}
参考资料
https://www.jianshu.com/p/9cf4a0dcb3b5
https://zhidao.baidu.com/question/1900127035845621380.html
https://blog.csdn.net/lgstudyvc/article/details/9337063
如有问题,欢迎指正。