多线程中线程执行时间严格控制到毫秒级

时间:2021-12-05 08:21:20
一个 实时曲线(采样频率50Hz)显示的问题,数据存在数据库中,要求曲线平滑。

采用多线程方式模拟实时曲线,就两个线程,一个读数据线程,一个更新曲线线程。



              Thread thread_GetDatabase = new Thread(GetDatabase);
                thread_GetDatabase.Start();

               Thread  thread_updatechart = new Thread(UpdateChart);
                thread_updatechart.Priority = ThreadPriority.Highest;
                thread_updatechart.Start();



 数据存放到共享队列中, 读数据线程写入队列, 更新曲线线程 从该队列读数据并显示。


 private static ConcurrentQueue<double> _ConcurrentQueueBuffer { get; set; }


读数据库线程,循环读取, 间隔时间500毫秒

private void GetDatabase()
        {        
                while (true)
                {               
                    GetDataFromOracle();          
                    Thread.Sleep(500);
                }          
        }


更新曲线线程,循环读取,间隔20毫秒

 private void UpdateChart()
        {
          while(true)
          {
                     update();          // 更新操作 更新操作的时间在0-1毫秒之间
                    Thread.Sleep(20);
           }
          }


问题: 长时间运行,更新曲线线程不能精确到20ms执行1次,大概1秒钟延迟2-3 毫秒,15分钟曲线延迟1秒钟,共享队列中的长度再逐步加大。随着时间推移,延迟越来越久,即取数据的线程的速度跟不上 读数据库的线程。

分析: 1. 可能 updatachart 线程的优先级有问题,后来设置为 highest优先级,还是一样。
              2. 读数据线程 GetDatabase 线程 间隔500ms 太小,导致 CPU对updatchart 线程切换不能精确到20ms, 调整到1000ms,但是会出现共享队列的数据不够,曲线停顿的情况。
 
请问 如何保证 updatchart 线程精确到20ms 执行一次?  画图用的是WPF的livechart控件。
谢谢各位!




19 个解决方案

#1


这种帖子还是早结了吧,没有精确这一说,像你这种用Thread.Sleep的更没有什么精确度可言

#2


用Timer会精确很多,不过误差总是会存在。20ms绘制一次,是不是可以考虑从设计上去改造?

#3


楼主不明白:用线程定时=定时不准。再高级别线程也不行。(随便晃一下鼠标时间就漂了)。
想精确定时换其它定时器吧(windows下用户级别定时器精读理论上最多到1毫秒)。

#4


听说timers误差十几二十毫秒   sleep更是不确定了

#5


引用 1 楼 AndroidJavaApp 的回复:
这种帖子还是早结了吧,没有精确这一说,像你这种用Thread.Sleep的更没有什么精确度可言


请问如何更精确呢?谢谢 。

#6


引用 2 楼 dongxinxi 的回复:
用Timer会精确很多,不过误差总是会存在。20ms绘制一次,是不是可以考虑从设计上去改造?


如果能从 采集软件直接读,我当然不会用这种方法,现在情况是只能读数据库,模拟曲线。


请问如何从设计考虑?谢谢

#7


引用 3 楼 xuggzu 的回复:
楼主不明白:用线程定时=定时不准。再高级别线程也不行。(随便晃一下鼠标时间就漂了)。
想精确定时换其它定时器吧(windows下用户级别定时器精读理论上最多到1毫秒)。


不用windows 平台 ,用啥呢?

#8


windows下,推荐楼主用多媒体定时器,精度5毫秒以下,具体用法百度即可。

#9


我记忆里初中生就应该明白一个点可以用(x,y)表示
然而……ConcurrentQueue<double> 多线程中线程执行时间严格控制到毫秒级
请问楼主什么学历?

#10


引用 4 楼 tys591320 的回复:
听说timers误差十几二十毫秒   sleep更是不确定了


那用timer还是会延迟啊,如何解决呢?谢谢

#11


引用 9 楼 shingoscar 的回复:
我记忆里初中生就应该明白一个点可以用(x,y)表示
然而……ConcurrentQueue<double> 多线程中线程执行时间严格控制到毫秒级
请问楼主什么学历?


不才,没你学历高。

时间信息,是利用系统时间累加的。数据库中偶尔有数据缺失,不能让曲线看起来不连贯。

清楚了没,博士?

#12


可以画成贝塞尔曲线,就连贯了

#13


引用 12 楼 liucqa 的回复:
可以画成贝塞尔曲线,就连贯了


厉害,几阶的?

#14


不使用 Thread.Sleep,线程一旦挂起,再次运行的时间就不由你控制了
你可以直接侦听系统时钟,并加以适当矫正

人眼的视觉残留是 0.1 秒,你每秒 50 帧 的更新速度,绝对可以保证看不出细微差异

#15


好好的技术帖 怎么说说就变成了装B帖了?

#16


引用 11 楼 conger_eel 的回复:
Quote: 引用 9 楼 shingoscar 的回复:

我记忆里初中生就应该明白一个点可以用(x,y)表示
然而……ConcurrentQueue<double> 多线程中线程执行时间严格控制到毫秒级
请问楼主什么学历?


不才,没你学历高。

时间信息,是利用系统时间累加的。数据库中偶尔有数据缺失,不能让曲线看起来不连贯。

清楚了没,博士?


我仔细读了两边才明白他的意思,他的疑问可能是一个点用x,y表示,应该是2个整形,但他没有弄懂你为什么用一个double,
ConcurrentQueue<double>

#17


多线程中线程执行时间严格控制到毫秒级你才延迟即毫秒 庆幸了吧你。。
windows默认10毫秒切换一次时间片 假设单核cpu下 电脑中一共10个线程 其中一个是你的 而且每个线程都需要占用大量的cpu时间 比如里面写上死循环 那么这种情况 每个线程都会把10毫秒用满 而轮询一圈时间片切换到你的线程的时候 已经过了90毫秒了 你觉得你里面的Sleep(20)有用?
当执行到你的Sleep的时候 那么系统认为你将放弃你所拥有的时间片 那么立即切换到其他线程去 一圈后回来 如果时间还没有到你的20毫秒 那么继续切放弃你的时间片 再回来的时候发现你的20毫秒已经到了 那么这个时候你的代码才会得到执行的机会 所以说你现在还觉得windows下面想要毫秒级别 还会准吗?尤其实在cpu使用率很高的情况下
有一种很不友好的很暴力的方式来 稍微提高一下精度就是:
时间 a = 当前时间;
while(当前时间 - a < 20毫秒){/*空循环把cpu的时间片占着 不要让他切走*/}
但是这样会让你的cpu使用率变高

但是上面的并不能解决你的问题 因为你只有10毫秒执行时间 结果你要执行20毫秒的空循环 所以时间片要到你两次才能把空循环执行完毕 还要等第三次时间片进入才能执行你真正想要执行的代码所以 你还白白浪费了20毫秒的cpu时间 如果说 你的延时比较小10毫秒内 那么到可以考虑一下上面的代码

#18


Window系统本身就不是一个实时的操作系统!

#19


引用 17 楼 crystal_lz 的回复:
多线程中线程执行时间严格控制到毫秒级你才延迟即毫秒 庆幸了吧你。。
windows默认10毫秒切换一次时间片 假设单核cpu下 电脑中一共10个线程 其中一个是你的 而且每个线程都需要占用大量的cpu时间 比如里面写上死循环 那么这种情况 每个线程都会把10毫秒用满 而轮询一圈时间片切换到你的线程的时候 已经过了90毫秒了 你觉得你里面的Sleep(20)有用?
当执行到你的Sleep的时候 那么系统认为你将放弃你所拥有的时间片 那么立即切换到其他线程去 一圈后回来 如果时间还没有到你的20毫秒 那么继续切放弃你的时间片 再回来的时候发现你的20毫秒已经到了 那么这个时候你的代码才会得到执行的机会 所以说你现在还觉得windows下面想要毫秒级别 还会准吗?尤其实在cpu使用率很高的情况下
有一种很不友好的很暴力的方式来 稍微提高一下精度就是:
时间 a = 当前时间;
while(当前时间 - a < 20毫秒){/*空循环把cpu的时间片占着 不要让他切走*/}
但是这样会让你的cpu使用率变高

但是上面的并不能解决你的问题 因为你只有10毫秒执行时间 结果你要执行20毫秒的空循环 所以时间片要到你两次才能把空循环执行完毕 还要等第三次时间片进入才能执行你真正想要执行的代码所以 你还白白浪费了20毫秒的cpu时间 如果说 你的延时比较小10毫秒内 那么到可以考虑一下上面的代码

谢谢,关键还是windwos 的调度机制不是实时的

#1


这种帖子还是早结了吧,没有精确这一说,像你这种用Thread.Sleep的更没有什么精确度可言

#2


用Timer会精确很多,不过误差总是会存在。20ms绘制一次,是不是可以考虑从设计上去改造?

#3


楼主不明白:用线程定时=定时不准。再高级别线程也不行。(随便晃一下鼠标时间就漂了)。
想精确定时换其它定时器吧(windows下用户级别定时器精读理论上最多到1毫秒)。

#4


听说timers误差十几二十毫秒   sleep更是不确定了

#5


引用 1 楼 AndroidJavaApp 的回复:
这种帖子还是早结了吧,没有精确这一说,像你这种用Thread.Sleep的更没有什么精确度可言


请问如何更精确呢?谢谢 。

#6


引用 2 楼 dongxinxi 的回复:
用Timer会精确很多,不过误差总是会存在。20ms绘制一次,是不是可以考虑从设计上去改造?


如果能从 采集软件直接读,我当然不会用这种方法,现在情况是只能读数据库,模拟曲线。


请问如何从设计考虑?谢谢

#7


引用 3 楼 xuggzu 的回复:
楼主不明白:用线程定时=定时不准。再高级别线程也不行。(随便晃一下鼠标时间就漂了)。
想精确定时换其它定时器吧(windows下用户级别定时器精读理论上最多到1毫秒)。


不用windows 平台 ,用啥呢?

#8


windows下,推荐楼主用多媒体定时器,精度5毫秒以下,具体用法百度即可。

#9


我记忆里初中生就应该明白一个点可以用(x,y)表示
然而……ConcurrentQueue<double> 多线程中线程执行时间严格控制到毫秒级
请问楼主什么学历?

#10


引用 4 楼 tys591320 的回复:
听说timers误差十几二十毫秒   sleep更是不确定了


那用timer还是会延迟啊,如何解决呢?谢谢

#11


引用 9 楼 shingoscar 的回复:
我记忆里初中生就应该明白一个点可以用(x,y)表示
然而……ConcurrentQueue<double> 多线程中线程执行时间严格控制到毫秒级
请问楼主什么学历?


不才,没你学历高。

时间信息,是利用系统时间累加的。数据库中偶尔有数据缺失,不能让曲线看起来不连贯。

清楚了没,博士?

#12


可以画成贝塞尔曲线,就连贯了

#13


引用 12 楼 liucqa 的回复:
可以画成贝塞尔曲线,就连贯了


厉害,几阶的?

#14


不使用 Thread.Sleep,线程一旦挂起,再次运行的时间就不由你控制了
你可以直接侦听系统时钟,并加以适当矫正

人眼的视觉残留是 0.1 秒,你每秒 50 帧 的更新速度,绝对可以保证看不出细微差异

#15


好好的技术帖 怎么说说就变成了装B帖了?

#16


引用 11 楼 conger_eel 的回复:
Quote: 引用 9 楼 shingoscar 的回复:

我记忆里初中生就应该明白一个点可以用(x,y)表示
然而……ConcurrentQueue<double> 多线程中线程执行时间严格控制到毫秒级
请问楼主什么学历?


不才,没你学历高。

时间信息,是利用系统时间累加的。数据库中偶尔有数据缺失,不能让曲线看起来不连贯。

清楚了没,博士?


我仔细读了两边才明白他的意思,他的疑问可能是一个点用x,y表示,应该是2个整形,但他没有弄懂你为什么用一个double,
ConcurrentQueue<double>

#17


多线程中线程执行时间严格控制到毫秒级你才延迟即毫秒 庆幸了吧你。。
windows默认10毫秒切换一次时间片 假设单核cpu下 电脑中一共10个线程 其中一个是你的 而且每个线程都需要占用大量的cpu时间 比如里面写上死循环 那么这种情况 每个线程都会把10毫秒用满 而轮询一圈时间片切换到你的线程的时候 已经过了90毫秒了 你觉得你里面的Sleep(20)有用?
当执行到你的Sleep的时候 那么系统认为你将放弃你所拥有的时间片 那么立即切换到其他线程去 一圈后回来 如果时间还没有到你的20毫秒 那么继续切放弃你的时间片 再回来的时候发现你的20毫秒已经到了 那么这个时候你的代码才会得到执行的机会 所以说你现在还觉得windows下面想要毫秒级别 还会准吗?尤其实在cpu使用率很高的情况下
有一种很不友好的很暴力的方式来 稍微提高一下精度就是:
时间 a = 当前时间;
while(当前时间 - a < 20毫秒){/*空循环把cpu的时间片占着 不要让他切走*/}
但是这样会让你的cpu使用率变高

但是上面的并不能解决你的问题 因为你只有10毫秒执行时间 结果你要执行20毫秒的空循环 所以时间片要到你两次才能把空循环执行完毕 还要等第三次时间片进入才能执行你真正想要执行的代码所以 你还白白浪费了20毫秒的cpu时间 如果说 你的延时比较小10毫秒内 那么到可以考虑一下上面的代码

#18


Window系统本身就不是一个实时的操作系统!

#19


引用 17 楼 crystal_lz 的回复:
多线程中线程执行时间严格控制到毫秒级你才延迟即毫秒 庆幸了吧你。。
windows默认10毫秒切换一次时间片 假设单核cpu下 电脑中一共10个线程 其中一个是你的 而且每个线程都需要占用大量的cpu时间 比如里面写上死循环 那么这种情况 每个线程都会把10毫秒用满 而轮询一圈时间片切换到你的线程的时候 已经过了90毫秒了 你觉得你里面的Sleep(20)有用?
当执行到你的Sleep的时候 那么系统认为你将放弃你所拥有的时间片 那么立即切换到其他线程去 一圈后回来 如果时间还没有到你的20毫秒 那么继续切放弃你的时间片 再回来的时候发现你的20毫秒已经到了 那么这个时候你的代码才会得到执行的机会 所以说你现在还觉得windows下面想要毫秒级别 还会准吗?尤其实在cpu使用率很高的情况下
有一种很不友好的很暴力的方式来 稍微提高一下精度就是:
时间 a = 当前时间;
while(当前时间 - a < 20毫秒){/*空循环把cpu的时间片占着 不要让他切走*/}
但是这样会让你的cpu使用率变高

但是上面的并不能解决你的问题 因为你只有10毫秒执行时间 结果你要执行20毫秒的空循环 所以时间片要到你两次才能把空循环执行完毕 还要等第三次时间片进入才能执行你真正想要执行的代码所以 你还白白浪费了20毫秒的cpu时间 如果说 你的延时比较小10毫秒内 那么到可以考虑一下上面的代码

谢谢,关键还是windwos 的调度机制不是实时的

#20