C#线程不会睡觉?
我有这个代码:
void Main() { System.Timers.Timer t = new System.Timers.Timer (1000); t.Enabled=true; t.Elapsed+= (sender, args) =>c(); Console.ReadLine(); } int h=0; public void c() { h++; new Thread(() => doWork(h)).Start(); } public void doWork(int h) { Thread.Sleep(3000); h.Dump(); }
我想知道如果间隔为1000毫秒,作业进程为3000毫秒,会发生什么情况。
不过,我看到一个奇怪的行为 – 3000毫秒的延迟只发生在一开始 !
我怎样才能使每个工作睡眠3000毫秒?
正如你在这里看到的,一开始有3秒的延迟,然后每次迭代1秒。
每当计时器滴答时,你开始一个线程做一些睡眠; 该线程完全隔离,计时器将继续每秒钟触发。 实际上, 即使将Sleep(3000)
移动到c()
,计时器也会每秒触发一次。
你目前拥有的是:
1000 tick (start thread A) 2000 tick (start thread B) 3000 tick (start thread C) 4000 tick (start thread D, A prints line) 5000 tick (start thread E, B prints line) 6000 tick (start thread F, C prints line) 7000 tick (start thread G, D prints line) 8000 tick (start thread H, E prints line) ...
目前还不清楚你想要做什么。 你可以禁止计时器,当你不希望它发射,并准备好后再次恢复,但是目前还不清楚Sleep()
的目的是什么。 另一个选项是一个只有一个Sleep()
的while
循环。 简单,并不涉及大量的线程。
每秒你都会以3秒的延迟开始新的线程。 它是这样发生的:
- 线程1开始
- 线程2启动,线程1睡眠
- 线程3开始,线程2睡眠,线程1睡眠
- 线程4开始,线程3睡眠,线程2睡眠,线程1睡眠
- 线程5开始,线程4睡眠,线程3睡眠,线程2睡眠,线程1转储
- 线程6开始,线程5睡眠,线程4睡眠,线程3睡眠,线程2转储
- 线程7开始,线程6睡眠,线程5睡眠,线程4睡眠,线程3倾倒
正如你所看到的,每个线程都会hibernate3秒钟,但每秒都会发生一次转储。
如何使用线程工作? 像这样行事:
void Main() { new Thread(() => doWork()).Start(); Console.ReadLine(); } public void doWork() { int h = 0; do { Thread.Sleep(3000); h.Dump(); h++; }while(true); }
你的例子非常有趣 – 它显示了并行处理的副作用。 为了回答你的问题,并使其更容易看到副作用,我稍微修改了你的例子:
void Main() { System.Timers.Timer t = new System.Timers.Timer (10); t.Enabled=true; t.Elapsed+= (sender, args) =>c(); Console.ReadLine(); } int t=0; int h=0; public void c() { h++; new Thread(() => doWork(h)).Start(); } public void doWork(int h2) { try { t++; string.Format("h={0}, h2={1}, threads={2} [start]", h, h2, t).Dump(); Thread.Sleep(3000); } finally { t--; string.Format("h={0}, h2={1}, threads={2} [end]", h, h2, t).Dump(); } }
我在这里修改的是以下内容:
- 定时器间隔现在是10毫秒,线程仍然有3000毫秒。 效果是,当线程正在hibernate时,将创build新的线程
- 我已经添加了varialbe
t
,它计算当前正在活动的线程的数量(在线程开始时增加,在线程结束之前减less) - 我已经添加了2个转储语句,打印出线程开始和线程结束
- 最后,我给函数
doWork
的参数一个不同的名字(h2),它允许看到底层variablesh的值
现在在LinqPad中看到这个修改过的程序的输出是有意思的 (注意值并不总是一样,因为它们取决于启动的线程的竞态条件):
h=1, h2=1, threads=1 [start] h=2, h2=2, threads=2 [start] h=3, h2=3, threads=3 [start] h=4, h2=4, threads=4 [start] h=5, h2=5, threads=5 [start] ... h=190, h2=190, threads=190 [start] h=191, h2=191, threads=191 [start] h=192, h2=192, threads=192 [start] h=193, h2=193, threads=193 [start] h=194, h2=194, threads=194 [start] h=194, h2=2, threads=192 [end] h=194, h2=1, threads=192 [end] h=194, h2=3, threads=191 [end] h=195, h2=195, threads=192 [start]
我认为价值观是自己说话的:现在发生的事情是,每10毫秒开始一个新线程,而其他线程还在睡觉。 另外有趣的是,看到h并不总是等于h2,特别是如果更多的线程在其他人正在睡觉时开始。 线数(variablest)在稳定之后,即在190-194左右运行。
例如,你可能会说,我们需要lockingvariablest和h
readonly object o1 = new object(); int _t=0; int t { get {int tmp=0; lock(o1) { tmp=_t; } return tmp; } set {lock(o1) { _t=value; }} }
虽然这是一个更清洁的方法,但并没有改变这个例子中显示的效果。
现在,为了certificate每个线程真的睡3000ms(= 3s),让我们添加一个Stopwatch
到工作线程doWork
:
public void doWork(int h2) { Stopwatch sw = new Stopwatch(); sw.Start(); try { t++; string.Format("h={0}, h2={1}, threads={2} [start]", h, h2, t).Dump(); Thread.Sleep(3000); } finally { sw.Stop(); var tim = sw.Elapsed; var elapsedMS = tim.Seconds*1000+tim.Milliseconds; t--; string.Format("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", h, h2, t, elapsedMS).Dump(); } }
为了正确清理线程,让我们在ReadLine
之后禁用定时器,如下所示:
Console.ReadLine(); t.Enabled=false;
这可以让你看到,如果没有更多的线程开始,按ENTER键后会发生什么:
... h=563, h2=559, threads=5 [end, sleep time=3105 ms] h=563, h2=561, threads=4 [end, sleep time=3073 ms] h=563, h2=558, threads=3 [end, sleep time=3117 ms] h=563, h2=560, threads=2 [end, sleep time=3085 ms] h=563, h2=562, threads=1 [end, sleep time=3054 ms] h=563, h2=563, threads=0 [end, sleep time=3053 ms]
你可以看到他们正在被一个接一个的按照预期的方式被终止,他们睡了大约3秒(或3000毫秒)。
您看到这种行为的原因很简单:您每秒安排一个新线程,并在三秒钟之后显示结果。 前四秒你什么都看不到。 那么三秒钟之前启动的线程就会转储; 另一个线程已经睡了两秒钟,又一个 – 一秒钟。 下一个第二个线程#2转储; 然后是线程#3,#4等等 – 每秒都会得到一个打印输出。
如果您希望每三秒钟看到一个打印输出,则应该每三秒钟计划一个新线程,并延迟一段时间:初始线程将在三秒内加上延时输出; 所有随后的线程将以三秒钟间隔发射。
似乎你每秒都在运行一个新的线程,这不是一个好主意,使用backgroundworker,当事件backgroundworker完成时再次调用C函数,这样你就不需要一个计时器
每个doWork都会睡三秒钟,但是他们的睡眠会重叠,因为您每隔一秒创build一次线程。