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. 线程1开始
  2. 线程2启动,线程1睡眠
  3. 线程3开始,线程2睡眠,线程1睡眠
  4. 线程4开始,线程3睡眠,线程2睡眠,线程1睡眠
  5. 线程5开始,线程4睡眠,线程3睡眠,线程2睡眠,线程1转储
  6. 线程6开始,线程5睡眠,线程4睡眠,线程3睡眠,线程2转储
  7. 线程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一次线程。