100165456.htm
来自「C#高级编程(第三版),顶死你们。。 。up」· HTM 代码 · 共 137 行 · 第 1/2 页
HTM
137 行
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">根据线程遇到不同语句的时间,可能会出现下述情况:第一个线程在</span><span lang="EN-US">a</span><span style="FONT-FAMILY: 宋体">上有一个锁,同时第二个线程在</span><span lang="EN-US">b</span><span style="FONT-FAMILY: 宋体">上有一个锁。不久,线程</span><span lang="EN-US">A</span><span style="FONT-FAMILY: 宋体">遇到</span><span lang="EN-US">lock(b)</span><span style="FONT-FAMILY: 宋体">语句,立即进人睡眠状态,等待</span><span lang="EN-US">b</span><span style="FONT-FAMILY: 宋体">上的锁被解开。之后,第二个线程遇到</span><span lang="EN-US">lock(a)</span><span style="FONT-FAMILY: 宋体">语句,也立即进人睡眠状态,等待</span><span lang="EN-US">Windows</span><span style="FONT-FAMILY: 宋体">在</span><span lang="EN-US">a</span><span style="FONT-FAMILY: 宋体">上的锁被解开时唤醒它。但</span><span lang="EN-US">a</span><span style="FONT-FAMILY: 宋体">上的锁永远不会解开,因为第一个线程拥有这个锁,目前正处于睡眠状态,在</span><span lang="EN-US">b</span><span style="FONT-FAMILY: 宋体">上的锁被解开前是不会醒的,而在第二个线程被叫醒之前,</span><span lang="EN-US">b</span><span style="FONT-FAMILY: 宋体">上的锁不会解开,结果就是一个死锁。两个线程都不会做任何事,而仅是等待另一个线程解开它们的锁。这类问题会使整个应用程序挂起,不能执行任何操作,除非使用“任务管理器”中断整个进程。</span></p>
<p class="a3" style="MARGIN-TOP: 8.15pt; FTEL: 21.45pt"><span style="FONT-FAMILY: 黑体">注意:</span></p>
<p class="a1" style="FTEL: 21.45pt"><span style="FONT-FAMILY: 楷体_GB2312">在这种情况下,另一个线程不可能解开锁:独占锁只能由定义它的线程解开。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">让两个线程以相同的顺序在对象上声明加锁,就可以避免发生死锁。在上面的示例中,如果第二个线程声明加锁的顺序与第一个线程相同,</span><span lang="EN-US">a</span><span style="FONT-FAMILY: 宋体">先</span><span lang="EN-US">b</span><span style="FONT-FAMILY: 宋体">后,则无论哪个线程先在</span><span lang="EN-US">a</span><span style="FONT-FAMILY: 宋体">上加锁,都会先完成它的任务后,才启动另一个线程。这样,就不会发生死锁了。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">在编码中很容易避免死锁,在上面的代码中,发生死锁是非常明显的,所以用户肯定不会编写这样的代码,但记住不同的锁可以发生在不同的方法调用中。在这个示例中,第一个线程实际执行下述代码:</span></p>
<p class="2" style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">lock (a)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> // do bits of processing </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> CallSomeMethod() </span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span lang="EN-US">CallSomeMethod()</span><span style="FONT-FAMILY: 宋体">可以调用其他方法,其中有一个</span><span lang="EN-US">lock(b)</span><span style="FONT-FAMILY: 宋体">语句,此时编写一段代码,则是否会发生死锁就不那么明显了。</span></p>
<p class="MsoNormal"><span lang="EN-US">(3) </span><span style="FONT-FAMILY: 宋体">竞态条件</span></p>
<p class="MsoNormal"><a ftel="raceconditions"><span style="FONT-FAMILY: 宋体">竞态条件比死锁更微妙。它很少中断进程的执行,但可能导致数据损坏。很难给竞态下一个准确的定义,但当几个线程试图访问同一个数据,但没有充分考虑其他线程的执行情况时,就会发生竞态。最好用一个示例来理解竞态条件。</span></a></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">假定有一个对象数组,其中每个元素都需要处理,现在使用许多线程来进行这种处理。假定有一个对象</span><span lang="EN-US">ArrayController</span><span style="FONT-FAMILY: 宋体">,它包含了对象数组和一个</span><span lang="EN-US">int</span><span style="FONT-FAMILY: 宋体">,该</span><span lang="EN-US">int</span><span style="FONT-FAMILY: 宋体">表示有多少对象已经处理完毕,下一次应处理哪一个对象。</span><span lang="EN-US">ArrayController</span><span style="FONT-FAMILY: 宋体">执行下述方法:</span></p>
<p class="2" style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">int GetObject(int index)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> // returns the object at the given index.</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">和一个读写属性:</span></p>
<p class="2" style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">int ObjectsProcessed</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> // indicates how many of the objects have been processed.</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">帮助处理对象的每个线程都执行下述代码:</span></p>
<p class="2" style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">lock(ArrayController)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> int nextIndex = ArrayController.ObjectsProcessed;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> Console.WriteLine("object to be processed next is " + index);</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> ++ArrayController.ObjectsProcessed;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> object next = ArrayController.GetObject();</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">ProcessObject(next);</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体; LETTER-SPACING: 0.3pt">这段代码可以工作,但假定为了避免资源被长期搁置不用,在显示用户信息时不在</span><span lang="EN-US" style="LETTER-SPACING: 0.3pt">ArrayController</span><span style="FONT-FAMILY: 宋体">上放置锁。因此,把上述代码重写为:</span></p>
<p class="2" style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">lock(ArrayController)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> int nextIndex = ArrayController.ObjectsProcessed;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">Console.WriteLine("object to be processed next is " + index);</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">lock(ArrayController)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> ++ArrayController.ObjectsProcessed;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US"> object next = ArrayController.GetObject();</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">ProcessObject(next);</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">现在可能有一个问题。在一个线程获得数组中的第</span><span lang="EN-US">11</span><span style="FONT-FAMILY: 宋体">个对象,并显示信息,说明它在处理该对象时,会发生什么?与此同时,第二个线程也开始执行相同的代码,调用</span><span lang="EN-US">ObjectsProcessed</span><span style="FONT-FAMILY: 宋体">,并确定要处理的下一个对象就是数组中的第</span><span lang="EN-US">11</span><span style="FONT-FAMILY: 宋体">个对象<span style="LETTER-SPACING: -1pt">——</span>因为第一个线程仍然还没有更新</span><span lang="EN-US">ArrayController.ObjectsProcessed</span><span style="FONT-FAMILY: 宋体">。在第二个线程告诉控制台,它正在处理第</span><span lang="EN-US">11</span><span style="FONT-FAMILY: 宋体">个对象时,第一个线程在</span><span lang="EN-US">ArrayController</span><span style="FONT-FAMILY: 宋体">上放置了另一个锁,并在这个锁内部递增了</span><span lang="EN-US">ObjectsProcessed</span><span style="FONT-FAMILY: 宋体">。但太迟了,这两个线程在处理同一个对象,此时的情形就称为竞态条件。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">对于死锁和竞态条件,出现这两种错误的条件常常不明显,如果有这样的条件,也很难识别错误。一般情况下,这需要一定的经验。但是,在编写多线程应用程序时,如果需要同步,就必须考虑代码的所有部分,检查是否有可能发生死锁或竞态条件。记住,不可能预见不同线程遇到不同语句的确切时间。</span></p></div>
<!-- page -->
<div class="page" style="text-align: center">
<a href="100165455.htm">上一页</a> <a href="index.html">首页</a> <a href="100165457.htm">下一页</a>
</div>
<div style="margin: 0px auto; width: 700px; border: solid 1px #0b5f98;">
<div style="float: left; width: 16px; background-color: #0b5f98; color: White; padding: 1px;">
图书导读
</div>
<div style="float: right; width: 670px; text-align: left; line-height: 16pt; padding-left: 2px">
<!--导读-->
<h1 id="divCurrentNode2" style="color: #b83507; width: 100%; text-align: left; font-size: 12px; padding-left: 2px">当前章节:<a href='100165456.htm'><font color='red'>15.6.2 同步问题</font></a></h1>
<div id="divRealteNod2" style="padding-left: 2px">
<div style='float:left;width:49%'>·<a href='100165453.htm'>15.5 线程的优先级</a></div><div style='float:right;width:49%'>·<a href='100165454.htm'>15.6 同步</a></div><div style='float:left;width:49%'>·<a href='100165455.htm'>15.6.1 同步的含义</a></div><div style='float:right;width:49%'>·<a href='100165457.htm'>15.7 小结</a></div><div style='float:left;width:49%'>·<a href='100165458.htm'>16.1 .NET Remoting的含义</a></div><div style='float:right;width:49%'>·<a href='100165459.htm'>16.1.1 应用程序类型和协议</a></div></div>
</div>
</div>
</div>
</div>
</body>
</html>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?