C#3.0核心技术-第19章线程-全新翻译注释-19.4

来源:互联网 发布:protools破解版下载mac 编辑:程序博客网 时间:2024/05/03 12:45

19.4 同步

到目前为止,我们已经描述了如何在一个线程上开始一个任务,如何配置一个线程,如何双向的传递数据。我们也描述了局部变量如何为一个线程所私有以及引用如何能够在线程之间被共享以允许它们(译者注:指线程)借助公共字段通信。

下一步是同步:为了一个可预测的结果协调线程的行为。当线程访问相同的数据时同步特别重要;在这个领域搁浅是惊人地容易(译者注:这句话的意思是在线程同步的编程中非常容易出错)。

同步构造能够被分为四个类别:

简单阻塞方法

这些(译者注:方法)等待另一个线程结束或者等待一段时间的流逝。Sleep、Join和EndInvoke是简单阻塞方法。

锁定构造

这些(译者注:构造)强制对资源的排它访问,例如一个字段或者代码段,确保在同一时间只有一个线程能够进入。锁定是首要的线程安全机制,允许线程访问公共数据而不互相干扰。锁定结构是lock和Mutex(以及一个被称为Semaphore的变种)。

信令构造

这些(译者注:构造)允许一个线程暂停直到从另外一个线程收到一个通知,避免对无效轮询的需要(译者注:指如果不使用信令构造,则需要通过轮询的方式来检查另一个线程的状态,以确定本线程是否可以继续前进)。这里有两个信令装置:事件等待句柄和Monitor的Wait/Pulse方法。

非阻塞同步构造

这些(译者注:构造)通过调用处理器原语保护对公共字段的访问。Interlocked类和volatile关键字是这个类别中的两个构造。

阻塞对除了最后一个类别以外的所有类别而言都是必不可少的。让我们简要地检查这个概念。

19.4.1 阻塞

一个线程当它的执行由于某种原因暂停时被认为是被阻塞了,例如当借助Join或者EndInvoke等待另外一个(译者注:线程)终止时。一个被阻塞的线程几乎不消耗处理器时间;CLR和操作系统了解被阻塞的线程,并提供恰当的支持以保持它们在一个休眠的状态,当它们的阻塞条件满足时唤醒它们。你能够借助于ThreadState属性测试一个被阻塞的线程:

clip_image001

原文注:

ThreadState是一个标志枚举,以按位方式组合了三“层”数据。然而,大多数值是冗余的、未使用的、或者过时的。下列代码剥离了一个ThreadState到四个有用值中的一个:Unstarted、Running、WaitSleepJoin、和Stopped:

clip_image002

译者注:

上面的代码内少了ThreadState.Running。

ThreadState属性对于诊断目的是有用的,但是不适合用于同步,因为一个线程状态在测试ThreadState和以此为依据进行动作之间可能发生变化。

19.4.2 阻塞对spinning

(译者注:spin原意是纺织、旋转的意思,用来描述一个在纺织机上不断旋转的纱锭。这里借用这个意思形容一段不断循环的代码,就像不断旋转的纱锭一样。)

有些时候,一个线程必须暂停直到一个特定条件满足。信令构造通过当它们的条件(译者注:指阻塞的条件)未满足时进行阻塞而有效地做到这一点。然而,这里有一个古怪的替代办法:一个线程可以通过在轮询循环中spinning来等待一个条件。例如:

clip_image003

或者:

clip_image004

这是对处理器时间极大的浪费:从CLR和操作系统的观点看,线程正在执行一个重要的计算,所以(译者注:线程)得到相应的被分配的资源!

某些时候,一个阻塞与spinning的混合可以被用来作为一个变种:

clip_image005

尽管不优雅,这比纯粹的spinning有效率的多。尽管由于proceed标志的并发问题能够导致问题(译者注:这里的意思是有可能在别的地方对proceed进行并发访问,而这里对proceed的访问没有任何限制措施,从而有可能导致问题)。恰当地使用锁定和信令来避免这个(译者注:指spinning)。

19.4.2.1 SpinWait

令人惊奇的是,Thread类提供了一个除了spin之外什么都不做的方法!SpinWait,不像Sleep,不阻塞或者放弃CPU。取而代之地,它无尽地循环,保持处理器“无用地忙碌”给定数量的迭代(译者注:这里也可以理解为循环)。尽管依据CPU速度和负载的不同而不同,50次迭代大概等于暂停1微秒。SpinWait很少被使用;它的首要目的是为了在预计极端短(1微秒之内)的时间内就会改变的资源或者字段上等待而不放弃处理器时间片。这个技术很少用在CLR和操作系统之外。