你的示例非常有趣,展示了并行处理的副作用。为了回答你的问题,并更容易地看到这些副作用,我稍微修改了你的示例:
using System;
using System.Threading;
using System.Diagnostics;
public class Program
{
public static void Main()
{
(new Example()).Main();
}
}
public class Example
{
public void Main()
{
System.Timers.Timer t = new System.Timers.Timer(10);
t.Enabled = true;
t.Elapsed += (sender, args) => c();
Console.ReadLine(); t.Enabled = false;
}
int t = 0;
int h = 0;
public void c()
{
h++;
new Thread(() => doWork(h)).Start();
}
public void doWork(int h2)
{
Stopwatch sw = new Stopwatch();
sw.Start();
try
{
t++;
Console.WriteLine("h={0}, h2={1}, threads={2} [start]", h, h2, t);
Thread.Sleep(3000);
}
finally
{
sw.Stop();
var tim = sw.Elapsed;
var elapsedMS = tim.Seconds * 1000 + tim.Milliseconds;
t--;
Console.WriteLine("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", h, h2, t, elapsedMS);
}
}
}
我所做的修改如下:
- 计时器间隔现在为10毫秒,线程仍然有3000毫秒。效果是,当线程正在休眠时,新线程将被创建
- 我添加了变量
t
,它计算当前活动线程的数量(当线程启动时增加,线程结束前减少)
- 我添加了2个转储语句,打印出线程开始和线程结束
- 最后,我给函数
doWork
的参数一个不同的名称(h2),这允许查看底层变量h的值
现在很有趣看到这个修改后的程序在
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,特别是当更多的线程在睡眠时启动时。线程数(变量t)在一段时间后稳定下来,即大约在190-194之间运行。
你可能会认为,我们需要在变量t和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; }}
}
虽然这种方式更加简洁,但它并没有改变本例中所显示的效果。
为了证明每个线程确实会休眠3000毫秒(即3秒),让我们在工作线程doWork
中添加一个Stopwatch
:
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()
这使你能够看到在你按下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毫秒)。