一、多线程介绍
C#通过多线程支持并行执行的代码。一个线程是一个独立执行的路径,可以同时与其他线程一起运行。一个C#客户端程序(Console,WPF,Winows Forms)开始于一个单独的线程,该线程由CLR和操作系统自动地创建,我们称它为主线程,而且可以通过创建附加的线程来实现多线程。
所有的例子都假设引入了以下的
namespaces:
Using System;
Using System.Threading;
1.初探
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Thread thread = new Thread(WriteY);//创建一个线程
6 thread.Start();//开始一个线程
7
8 for (int i = 0; i < 1000; i++)//主线程执行循环
9 {
10 Console.Write("x");
11 }
12
13 Console.ReadLine();
14 }
15 static void WriteY()
16 {
17 for (int i = 0; i < 1000; i++)
18 {
19 Console.Write("y");
20 }
21 }
22
23 }
一旦开始,一个线程的IsAlive属性返回true,直到这个线程结束。当传递给线程的构造函数的委托完成执行时,这个线程结束。一旦结束,这个线程不能重启。
2.内存隔离
CLR给每个线程分配自己内存栈,因此局部变量可以保持分离。在下一个例子中,我们定义了一个使用局部变量的方法,然后在主线程和子线程同时调用这个方法。 因为每个线程的内存栈都有一份隔离的循环变量的拷贝,因此可以推断出,输出结果是10个“y”字符 。
3.数据共享如果多个线程对同一个对象实例有相同的引用,这些线程就共享这个对象实例的数据。例如:
1 class Program
2 {
3 bool done = false;
4 static void Main(string[] args)
5 {
6 Program p= new Program();
7 new Thread(p.Go).Start();
8 p.Go();
9 Console.ReadKey();
10 }
11
12 void Go()
13 {
14 if (!done)
15 {
16 done = true;
17 Console.WriteLine("Done");
18 }
19 }
20 }
因为两个线程都调用实例p的go的方法,
因此他们共享done这个字段,结果是done只打印出一次而不是两次。静态字段提供另外一种共享数据的方法:
1 class ThreadTest
2 {
3 static bool done; // Static fields are shared between all threads
4
5 static void Main()
6 {
7 new Thread (Go).Start();
8 Go();
9 }
10
11 static void Go()
12 {
13 if (!done) { done = true; Console.WriteLine ("Done"); }
14 }
15 }
4.线程安全这两个例子展示了另外一个重要的概念:线程安全确实是不确定的:done可能被打印出两次(尽管是不太可能发生的)。当我们把Go方法中的语句的顺序交换下,打印出两次done的几率显著提升。
1 class Program
2 {
3 static bool done = false;
4 static void Main(string[] args)
5 {
6 Program p = new Program();
7 new Thread(p.Go).Start();
8 p.Go();
9 Console.ReadKey();
10 }
11
12 void Go()
13 {
14 if (!done)
15 {
16 Console.WriteLine("Done");
17 done = true;
18 }
19 }
20 }
这个地方的问题是线程A在线程B设置done等于true之前进入if条件判断中,所有A有机会打印出"Done"。改进方式当读\写一个公共字段时,获取一个独占锁(exclusive lock)。C#提供了关键字lock。
1 class Program
2 {
3 static bool done = false;
4 static readonly object locker = new object();
5 static void Main(string[] args)
6 {
7 new Thread(Go).Start();
8 Go();
9 Console.ReadKey();
10 }
11
12 static void Go()
13 {
14 lock (locker)
15 {
16 if (!done)
17 {
18 Console.WriteLine("Done");
19 done = true;
20 }
21 }
22 }
23 }
当两个线程同时抢占一个锁时(在这个例子中,locker),一个线程等待,或者阻塞,知道这个锁释放。在这个例子中,这个锁保证一次只有一个线程可以进入代码的临界区域,然后“Done”只会被打印一次。代码在这种不确定的多线程背景下中被保护被叫做线程安全。
注意:在多线程中,共享数据是造成复杂原因的主要,而且会产生让人费解的错误。尽管很基本但还是要尽可能保持简单。
一个线程,当阻塞的时候,不占用CPU资源。
二 Join和Sleep
1.Join
通过调用一个线程的Join方法,可以等待另外一个线程结束。例如:
接下来第二页