77范文网 - 专业文章范例文档资料分享平台

线程与进程区别(4)

来源:网络收集 时间:2018-12-27 下载这篇文档 手机版
说明:文章内容仅供预览,部分内容可能不全,需要完整文档或者需要复制内容,请下载word后使用。下载word有问题请添加微信号:或QQ: 处理(尽可能给您提供完整文档),感谢您的支持与谅解。点击这里给我发消息

应该意识到,对程序中资源的访问进行同步时,其难点来自于是使用细粒度锁还是粗粒度锁这个两难的选择。如果在访问资源时采用粗粒度的同步方式,虽然可以简化代码但是也会把自己暴露在争用瓶颈的问题上。如果粒度过细,代码又会变的很复杂,以至于维护工作令人生厌。然后又会遇上死锁和竞态条件这些在下面章节将要介绍的问题。

因此在我们开始谈论有关同步机制之前,有必要先了解一下有关竞态条件和死锁的概念。 5.4.1 竞态条件

竞态条件指的是一种特殊的情况,在这种情况下各个执行单元以一种没有逻辑的顺序执行动作,从而导致意想不到的结果。

举一个例子,线程T修改资源R后,释放了它对R的写访问权,之后又重新夺回R的读访问权再使用它,并以为它的状态仍然保持在它释放它之后的状态。但是在写访问权释放后到重新夺回读访问权的这段时间间隔中,可能另一个线程已经修改了R的状态。

另一个经典的竞态条件的例子就是生产者/消费者模型。生产者通常使用同一个物理内存空间保存被生产的信息。一般说来,我们不会忘记在生产者与消费者的并发访问之间保护这个空间。容易被我们忘记的是生产者必须确保在生产新信息前,旧的信息已被消费者所读取。如果我们没有采取相应的预防措施,我们将面临生产的信息从未被消费的危险。

如果静态条件没有被妥善的管理,将导致安全系统的漏洞。同一个应用程序的另一个实例很可能会引发一系列开发者所预计不到的事件。一般来说,必须对那种用于确认身份鉴别结果的布尔量的写访问做最完善的保护。如果没有这么做,那么在它的状态被身份鉴别机制设置后,到它被读取以保护对资源的访问的这段时间内,很有可能已经被修改了。已知的安全漏洞很多都归咎于对静态条件不恰当的管理。其中之一甚至影响了Unix操作系统的内核。

5.4.2 死锁

死锁指的是由于两个或多个执行单元之间相互等待对方结束而引起阻塞的情况。例如:

一个线程T1获得了对资源R1的访问权。 一个线程T2获得了对资源R2的访问权。

T1请求对R2的访问权但是由于此权力被T2所占而不得不等待。 T2请求对R1的访问权但是由于此权力被T1所占而不得不等待。

T1和T2将永远维持等待状态,此时我们陷入了死锁的处境!这种问题比你所遇到的大多数的bug都要隐秘,针对此问题主要有三种解决方案:

? ? ?

在同一时刻不允许一个线程访问多个资源。

为资源访问权的获取定义一个关系顺序。换句话说,当一个线程已经获得了R1的访问权后,将无法获得R2的访问权。当然,访问权的释放必须遵循相反的顺序。 为所有访问资源的请求系统地定义一个最大等待时间(超时时间),并妥善处理请求失败的情况。几乎所有的.NET的同步机制都提供了这个功能。

前两种技术效率更高但是也更加难于实现。事实上,它们都需要很强的约束,而这点随着应用程序的演变将越来越难以维护。尽管如此,使用这些技术不会存在失败的情况。

大的项目通常使用第三种方法。事实上,如果项目很大,一般来说它会使用大量的资源。在这种情况下,资源之间发生冲突的概率很低,也就意味着失败的情况会比较罕见。我们认为这是一种乐观的方法。秉着同样的精神,我们在19.5节描述了一种乐观的数据库访问模型。

5.5 使用volatile字段与Interlocked类实现同步

5.5.1 volatile字段

volatile字段可以被多个线程访问。我们假设这些访问没有做任何同步。在这种情况下,CLR中一些用于管理代码和内存的内部机制将负责同步工作,但是此时不能确保对该字段读访问总能读取到最新的值,而声明为volatile的字段则能提供这样的保证。在C#中,如果一个字段在它的声明前使用了volatile关键字,则该字段被声明为volatile。

不是所有的字段都可以成为volatile,成为这种类型的字段有一个条件。如果一个字段要成为volatile,它的类型必须是以下所列的类型中的一种:

? ? ? ?

引用类型(这里只有访问该类型的引用是同步的,访问其成员并不同步)。 一个指针(在不安全的代码块中)。

sbyte、byte、short、ushort、int、uint、char、float、bool(工作在64位处理器上时为double、long与ulong)。

一个使用以下底层类型的枚举类型:byte、sbyte、short、ushort、int、uint(工作在64位的处理器上时为double、long与ulong)。

你可能已经注意到了,只有值或者引用的位数不超过本机整型值的位数(4或8由底层处理器决定)的类型才能成为volatile。这意味着对更大的值类型进行并发访问必须进行同步,下面我们将会对此进行讨论。

5.5.2 System.Threading.Interlocked类

经验显示,那些需要在多线程情况下被保护的资源通常是整型值,而这些被共享的整型值最常见的操作就是增加/减少以及相加。.NETFramework利用

System.Threading.Interlocked类提供了一个专门的机制用于完成这些特定的操作。这个类提供了Increment()、Decrement()与Add()三个静态方法,分别用于对int或者long类型变量的递增、递减与相加操作,这些变量以引用方式作为参数传入。我们认为使用Interlocked类让这些操作具有了原子性。

下面的程序显示了两个线程如何并发访问一个名为counter的整型变量。一个线程将其递增5次,另一个将其递减5次。 例5-5

该程序输出(以非确定方式输出,意味着每执行一次显示的结果都是不同的):

如果我们不让这些线程在每次修改变量后休眠10毫秒,那么它们将有足够的时间在一个时间片中完成它们的任务,那样也就不会出现交叉操作,更不用说并发访问了。

5.5.3 Interlocked类提供的其他功能

Interlocked类还允许使用Exchange()静态方法,以原子操作的形式交换某些变量的状态。还可以使用CompareExchange()静态方法在满足一个特定条件的基础上以原子操作的形式交换两个值。

5.6 使用System.Threading.Monitor类与C#的lock关键字实现同步

以原子操作的方式完成简单的操作无疑是很重要的,但是这还远不能涵盖所有需要用到同步的事例。System.Threading.Monitor类几乎允许将任意一段代码设置为在某个时间仅能被一个线程执行。我们将这段代码称之为临界区。

5.6.1 Enter()方法和Exit()方法

Monitor类提供了Enter(object)与Exit(object)这两个静态方法。这两个方法以一个对象作为参数,该对象提供了一个简单的方式用于唯一标识那个将以同步方式访问的资源。当一个线程调用了Enter()方法,它将等待以获得访问该引用对象的独占权(仅当另一个线程拥有该权力的时候它才会等待)。一旦该权力被获得并使用,线程可以对同一个对象调用Exit()方法以释放该权力。

一个线程可以对同一个对象多次调用Enter(),只要对同一对象调用相同次数的Exit()来释放独占访问权。

一个线程也可以在同一时间拥有多个对象的独占权,但是这样会产生死锁的情况。

绝不能对一个值类型的实例调用Enter()与Exit()方法。

不管发生了什么,必须在finally子句中调用Exit()以释放所有的独占访问权。 如果在例5-5中,一个线程非要将counter做一次平方而另一个线程非要将counter乘2,我们就不得不用Monitor类去替换对Interlocked类的使用。f1()与f2()的代码将变成下面这样: 例5-6[1]

人们很容易想到用counter来代替typeof(Program),但是counter是一个值类型的静态成员。需要注意平方和倍增操作是不满足交换律的,所以counter的最终结果是非确定性的。

5.6.2 C#的lock关键字

C#语言通过lock关键字提供了一种比使用Enter()和Exit()方法更加简洁的选择。我们的程序可以改写为下面这个样子: 例5-7

百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库线程与进程区别(4)在线全文阅读。

线程与进程区别(4).doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印 下载失败或者文档不完整,请联系客服人员解决!
本文链接:https://www.77cn.com.cn/wenku/zonghe/391975.html(转载请注明文章来源)
Copyright © 2008-2022 免费范文网 版权所有
声明 :本网站尊重并保护知识产权,根据《信息网络传播权保护条例》,如果我们转载的作品侵犯了您的权利,请在一个月内通知我们,我们会及时删除。
客服QQ: 邮箱:tiandhx2@hotmail.com
苏ICP备16052595号-18
× 注册会员免费下载(下载后可以自由复制和排版)
注册会员下载
全站内容免费自由复制
注册会员下载
全站内容免费自由复制
注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: