1. 概述与概念
1.1. 入门线程小例子
C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多线程创建额外的线程。
class ThreadTest
{
static void Main()
{
Threadt = new Thread (WriteY);
t.Start();
while (true) Console.Write ("x");
}
static void WriteY()
{
while (true)Console.Write ("y");
}
}
主线程创建了一个新线程“t”,它运行了一个重复打印字母"y"的方法,同时主线程重复打印字母“x”。CLR分配每个线程到它自己的内存堆栈上,来保证局部变量的分离运行。在接下来的方法中我们定义了一个局部变量,然后在主线程和新创建的线程上同时地调用这个方法。
static void Main()
{
new Thread (Go).Start();
Go();
}
static void Go()
{
for (int cycles = 0; cycles < 5; cycles++)Console.Write ('?');
}
变量cycles的副本分别在各自的内存堆栈中创建,输出也一样,可预见,会有10个问号输出。当线程们引用了一些公用的目标实例的时候,他们会共享数据。下面是实例:
class ThreadTest
{
bool done;
static void Main()
{
ThreadTest tt = new ThreadTest();
new Thread (tt.Go).Start();
tt.Go();
}
void Go()
{
if (!done) { done = true; Console.WriteLine("Done"); }
}
}
因为在相同的ThreadTest实例中,两个线程都调用了Go(),它们共享了done字段,这个结果输出的是一个"Done",而不是两个。
静态字段提供了另一种在线程间共享数据的方式,下面是一个以done为静态字段的例子:
class ThreadTest
{
static bool done;
static void Main()
{
new Thread (Go).Start();
Go();
}
static void Go()
{
if (!done) { done = true; Console.WriteLine("Done"); }
}
}
上述两个例子足以说明, 另一个关键概念, 那就是线程安全(或反之,它的不足之处! ) 输出实际上是不确定的:它可能(虽然不大可能) ,"Done" ,可以被打印两次。然而,如果我们在Go方法里调换指令的顺序,"Done"被打印两次的机会会大幅地上升:
static void Go()
{
if (!done){ Console.WriteLine ("Done"); done = true; }
}