2024年4月2日发(作者:)

C#中的多线程

By Joseph Albahari, Translated by Swanky Wu

Based on "C# 3.0 in a Nutshell" by

Joseph Albahari and Ben Albahari (O'Reilly Media)

/nutshell/

入门 线程同步基础

使用多线程

单元模式和Windows

Forms

BackgroundWorker类

ReaderWriterLock类

线程池

异步委托

计时器

局部储存

高级话题

非阻止同步

Wait和Pulse

Suspend和Resume

终止线程

概述与概念 同步要领

创建和开始使用多线程 锁和线程安全

Interrupt 和 Abort

线程状态

等待句柄

同步环境

概述与概念

C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个

C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多

线程创建额外的线程。这里的一个简单的例子及其输出:

除非被指定,否则所有的例子都假定以下命名空间被引用了:

using System;

using ing;

class ThreadTest {

static void Main() {

Thread t = new Thread (WriteY);

(); // 在新的线程中运行WriteY

while (true) ("x"); // 不停地写'x'

}

static void WriteY() {

while (true) ("y"); // 不停地写'y'

}

}

xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyy

yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxx

xxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

yyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

...

主线程创建了一个新线程“t”,它运行了一个重复打印字母"y"的方法,同时主线程重复但因字母“x”。

CLR分配每个线程到它自己的内存堆栈上,来保证局部变量的分离运行。在接下来的方法中我们定义了一

个局部变量,然后在主线程和新创建的线程上同时地调用这个方法。

static void Main() {

new Thread (Go).Start(); // 调用Go()方法在一个新线程中

Go(); // 在主线程中调用Go()

}

static void Go() {

// 声明和使用一个局部变量'cycles'

for (int cycles = 0; cycles < 5; cycles++) ('?');

}

??????????

变量cycles的副本分别在各自的内存堆栈中创建,输出也一样,可预见,会有10个问号输出。当线程们引

用了一些公用的目标实例的时候,他们会共享数据。下面是实例:

class ThreadTest {

bool done;

static void Main() {

ThreadTest tt = new ThreadTest(); // 创建一个实例

new Thread ().Start();

();

}

// 注意Go现在是一个实例方法

void Go() {

if (!done) { done = true; ine ("Done"); }

}

}

因为在相同的ThreadTest实例中,两个线程都调用了Go(),它们共享了done字段,这个结果输出的是一

个"Done",而不是两个。

Done

静态字段提供了另一种在线程间共享数据的方式,下面是一个以done为静态字段的例子:

class ThreadTest {

static bool done; // 静态方法被所有 线程一块使用

static void Main() {

new Thread (Go).Start();

Go();

}

static void Go() {

if (!done) { done = true; ine ("Done"); }

}

}

上述两个例子足以说明, 另一个关键概念, 那就是

线程安全

(或反之,它的不足之处! ) 输出实际上是不

确定的:它可能(虽然不大可能) , "Done" ,可以被打印两次。然而,如果我们在Go方法里调换指令的顺

序, "Done"被打印两次的机会会大幅地上升:

static void Go() {