相信很多人在实际开发中是不愿使用到多线程的,因为一旦引入多线程这个概念,对应功能就需要加很多关于线程的考虑措施,如锁,任务回调顺序等等。有事加了一些对应的措施,还是感觉程序出现偶发的不同问题,这里主要记录一下多线程任务时需要注意的一些毫秒相关的事情。
for (int i = 0; i < 8; i++) { Thread.Sleep(1000); Console.WriteLine($"{DateTime.Now.ToString("mm:ss.fff")},i={i}"); }
这段代码模拟一个循环执行8次的任务,实际可能可能有一些功能会和这个比较类似,如设备模块初始化,一些耗时计算等等。
Console.WriteLine作为记录一个任务执行结束的时间记录
单线程时i的输出顺序还是正常的,同时这种多批次任务时会发现耗时还是比较久的,所以会考虑到异步如await anysc,task,thread等待,这里以task为例
for (int i = 0; i < 8; i++) { Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"{DateTime.Now.ToString("mm:ss.fff")},i={i}"); }); }
如果i作为传递的运算参数,就会发现调试运算时i产生莫名奇妙的问题,如顺序错乱,i值异常等等,如
通过输出i会发现i清一色的为8,我这里还只是比较简单的输出i,如果此时i作为比较重要的运算参数就会使程序产生莫名其妙的逻辑问题,这里主要是因为线程它的执行逻辑不是立即触发。
在底层逻辑中他也是以任务的方式存放在线程池中,在处理器有空余的核心时才会分配任务执行的资源。甚至说for循环次数都执行完毕分出去的8个任务还没有一个开始执行,此时多线程同时访问变量 i,可能会导致闭包问题,因为每个线程都在访问相同的 i 变量。当线程在访问 i 变量时,可能会发生 i 的值在迭代过程中已经发生了变化,这可能会导致输出的 i 值不是期望值
改进措施
for (int i = 0; i < 8; i++) { int value = i; Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"{DateTime.Now.ToString("mm:ss.fff")},i={value}"); }); }
此时通过在循环内部创建一个局部变量 value,并将 i 的值赋给它,可以确保每个线程都使用正确且的 i 值。但是这样同时就会伴随一个task任务时间执行顺序错乱的问题如
这里我使用datetime输出时间精细度只能到微秒,但是还是能感知到cpu分配核心处理资源的时间不是一样的,想看更细的时间颗粒度,可以转换成ticks去输出感知。
此时如果我们因为耗时任务想要减少耗时,从而同时派出8个任务,还需要他们按照顺序回来但是因为任务执行时间的不统一,所以在任务执行完毕的关键点需要以排队的形式接收分配出去任务的返回结果,从而减少多任务带来的耗时与多线程带来的数据错乱。
最后,既然上面都观察到时间精细到微秒的误差了,可以跳转到第一个单一线程执行任务图
第一次任务执行时间精确到毫秒是656,但最后一次执行结果时间为729,设计层面中执行8次任务设计的任务耗时都是1000ms,在最后一次执行毫秒刻度也应该与第一次保持一致都为656,但是由于cpu核心分配资源调度问题从而产生了一些时间刻度的误差。
这种时间误差可能在绝大部分时间都不需要考虑,但是牵扯到一些精细化的数据,此时的误差就是比较重要的问题了。任务的性能,耗时都需要一个比较好的解决方案了。
标签:多线程,Console,C#,耗时,任务,杂记,线程,执行 From: https://www.cnblogs.com/Zjl-NanKe/p/18096823