`
isiqi
  • 浏览: 16083283 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

双 IF 魔符

阅读更多

转自IBMdeveloperWorks中国

如果您发现您的代码99.99%的时间在单CPU上运行,但是当您按比例增加到两个或更多个CPU时,它很快就会崩溃,那么这一珍品正适合您。

这一问题不仅与Java代码在一个对称多处理器(symmetricmultipleprocessor,SMP)平台上运行的复杂程度有关,还与管道技术对“受保护的(protected)”代码的影响有关。尽管您的代码应该高效而且一致始终都很重要,但是由于SMP这样的平台将夸大存储模型中的一致性问题,因此,当您的应用程序在SMP平台上运行时尤其是如此。

虽然双if子句是解决多线程应用程序所带来的问题的一般方法,但它使您遇到“弱一致性”模型(CPU管道技术、预测执行(speculativeexecution)等等)所带来的问题(尤其是关于SMP系统)。因此,这个珍品简而言之就是:如果您的应用程序将在SMP系统上运行,那么请不要在您的代码中使用双if逻辑。现在,让我们仔细看一下这个问题的原因和机理。

双if逻辑
首先,请考虑一下把指针指向资源的下列代码,其中flag表示有无该资源:

IFflag==0
{
//noresource
setflag
acquireresource
setpointertoresource
}
ELSE
setpointertoresource

如果这段代码是可重入的(即,可以由多个线程运行它),那么它是非常危险的。请考虑一下如果一个线程在if子句的中途,此时另外一个线程发现flag已经被设置了,于是它就试图去设置一个指针指向第一个线程还未分配的资源,结果会怎样呢?幸运的是,这个问题很容易纠正,如下所示:

IFflag==0
{
//noresource
acquireresource-----A
setpointertoresource
setflag
}
ELSE
setpointertoresource

但是,现在我们又发现另外一个问题:一个线程阻塞在A点,另一个线程却因发现flag被设为0而继续执行。在这种情况下,这两个线程都执行同一段代码分配资源—这根本不是我们所希望的!然而,解决方法又是众所周知:我们只要在执行获取资源的代码时,封锁所有其它线程,如下所示:

ENTERLOCK
IFflag==0
{
//noresource
acquireresource
setpointertoresource
setflag
}
ELSE
setpointertoresource
LEAVELOCK

(请注意我使用了LOCK这一术语来保持示例简单。当然,在Java代码中是用synchronize子句内的同步代码来表示它)。

现在我们的代码是线程安全的,但还不是高效的。获取锁(或Java术语中的管程(monitor))需要很多时间,而且锁中的代码比我们需要的要多。为了纠正这一点,我们把代码改写成下面这样:

IFflag==0
{
//noresource-----A
ENTERLOCK
acquireresource
setpointertoresource
setflag
LEAVELOCK
}
ELSE
setpointertoresource

这几乎奏效了,但是我们又引入了前面的问题,一个线程在A点被中止,而另一个线程插入进来,因此造成了CPU的混乱。为了纠正这一点,我们使用著名的双if子句:

IFflag==0
{
//noresource-----A
ENTERLOCK
IFflag==0
acquireresource
setpointertoresource
setflag
ELSE
setpointertoresource
LEAVELOCK
}
ELSE
setpointertoresource

通过添加双if子句,我们已经尽了最大努力使代码线程安全而且高效。许多高级编程技术方面的书中都推荐使用双if逻辑来解决线程争用。但是双if逻辑在多个线程可以同时执行的SMP机器上并不安全。

您会说,可是某一时刻在“锁住的”那部分代码中只会有一个线程,那么会出什么问题呢?很多!这就是把这一技巧叫做“双if魔符”的原因所在。

双if魔符请考虑一下管道技术对上面这段代码的影响:

由于CPU看不到任何依赖,所以可以以任何次序执行if子句里的代码。这会影响到下面用*标出的指令。

如果flag被设为0,那么与先计算“flag”再计算flag非0情况下的指针相比,计算指针(可能会是垃圾)并抛掉它可能是更高效的做法。所以,CPU可以采用管道技术处理下面用**标出的指令。
以下又是这些代码,为说明上述观点而加上了标记:

**IFflag==0
{
//noresource
ENTERLOCK
IFflag==0
*acquireresource
*setpointertoresource
*setflag
ELSE
setpointertoresource
LEAVELOCK
}
ELSE
**setpointertoresource


尽管这似乎有点奇怪,但上面的代码可能会象下面这样执行:

**setpointertoresource
**IFflag==0
{
//noresource
ENTERLOCK
setpointertoresource
IFflag==0
*setflag
*acquireresource-----A
*setpointertoresource
ELSE

LEAVELOCK
}
ELSE

正如您所见到的,我们很敏感的代码仍在锁中,但是现在在分配资源之前设置flag。因此,请想象一下线程A正在A点附近执行,此刻另外一个线程到来了。在SMP机器上,第二个线程可以在另一个CPU上与第一个线程同时执行;这个线程看到flag没有被设置成0(因为它在锁的外部),它可以继续把指针指向未分配资源—上当了!

此外,即使代码按我们所写的执行,我们仍然没有免除灾难,原因在于CPU可以采用管道技术处理以上用**标出的代码:

线程A进入锁住的那部分代码。

线程B对第一个if语句求值并计算指针的值(在最后的else子句中)。

线程B得到“指针”(由于线程A还没有指定它,所以是垃圾)的值。

在线程A完成并解锁的同时,线程B将计算第一个if的值。

解锁导致“flag”的值被清除(意味着线程B在它的高速缓存中具有的任何值都是无效的)。

线程B计算好第一个if子句的值,当然,现在它发现值为true。

现在线程B使用前面那个是垃圾的指针值。

这就是Java双if魔符。虽然其它的语言(比如C/C++)让您通过使用语言本机功能强制执行的次序来解决这个问题,Java语言却不行。为了安全的执行上述示例,您必须把所有代码封装在一个synchronize子句中,或者寻找另外一种写法。

分享到:
评论

相关推荐

    C语言程序设计-双分支的if语句.pptx

    1-2 双分支的if语句 if (表达式) 语句1; else 语句2; if(成绩大于等于60分) { 及格; } else { 不及格; } if和else是成对出现的,else不能单独使用,必须和if配对使用。 else和后面的语句2之间没有分号”;”,...

    if命令IF %ERRORLEVEL% LEQ 1 goto okay

    执行批处理程序中的条件处理。 IF [NOT] ERRORLEVEL number command IF [NOT] string1==string2 command IF [NOT] EXIST filename command NOT 指定只有条件为 false 的情况...字串符表达式;否则,您会得到其数值。

    IF综合工具V5.0.9

    IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合工具V5.0.9IF综合...

    双路径决策--If语句

    vb三大流程之一--关于双路径决策IF语句的教案

    IF函数的所有公式(入门级*进阶级*高级)

    一、IF函数的使用方法(入门级) 1、单条件判断返回值 =IF(A1>20,"完成任务","未完成") 2、多重条件判断 =IF(A1="101","现金",IF(A1="1121","应收票据",IF(A1=1403,"原材料"))) 注:多条件判断时,注意括号的位置...

    C语言入门习题:if双分支结构简单应用

    【问题描述】 输入一个整数,如果输入的数是偶数或者负数,则输出其平方,否则输出其一半的值。 【输入形式】 整型数据 【输出形式】 整型数据 【样例输入】 7 【样例输出】 3 ...正确性

    Oracle中PL/SQL中if语句的写法介绍

    代码如下:/*If语句:判断用户输入的数字。*/set serveroutput on –接收键盘输入accept num prompt ‘请输入一个数字:’; declare –将屏幕输入的数字付给变量 pnum number := #begin if pnum = 0 then dbms_...

    JSTL中if标签使用方法

    <c:if> <c:if>的用途就和我们一般在程序中用的if一样。  语法  语法1:没有本体内容(body)  <c:if test="testCondition" var="varName" [scope="{page|request|session|application}"]/>  语法2:有本体内容  ...

    Java程序设计基础:if单分支语句.pptx

    双分支if-else语句 多分支if-else-if语句 单分支if语句 if选择结构 单分支if语句的语法 语法: if(条件){ //语句序列 } false true 条件 语句序列 “单路条件”选择结构流程图 说明: ”条件”的结果必须是布尔值...

    USB报告描述符自动生成工具 HID Descriptor tool

    HID Descriptor tool USB报告描述符自动生成工具 SMT32编码自动生成工具 HID tool

    if Swich例子,C#源代码//测试if和else

    if Swich例子,C#C#源代码//测试if和else void testIfElse() { if (age >= 22) { Console.WriteLine( "if-else测试:\t{0}有{1}岁,可以结婚。", name, age); } else if ((age ) && (age >= 18)) { Console...

    if的4种用法

    if的4种用法

    c语言if语句实例_C语言if语句和switch语句实例_If..._

    1、.了解C语言的逻辑运算2、.学会使用if语句和switch语句3、.熟练掌握选择结构程序设计方法

    usb if测试工具

    微软官网上找到的 徽标认证工具 usb-if测试

    IAPWS-IF97.zip_IAPWS- IF97_IF97_c_iapws-if97_水蒸汽

    IAPWS-IF97 c语言实现 计算水或水蒸汽各性质

    VS2015 if else for 代码折叠

    VS2015 if else for 代码折叠,VS2015 if else for 代码折叠,VS2015 if else for 代码折叠,VS2015 if else for 代码折叠,VS2015 if else for 代码折叠

    Linux if语句详解

    详细讲解了Linux中if语句的使用 脚本示例: #!/bin/bash # This script prints a message about your weight if you give it your # weight in kilos and hight in centimeters. if [ ! $# == 2 ]; then echo "Usage...

    Java基础if和ifelse选择结构

    Java基础if机构

    TIA博途WINCC的触摸屏VB脚本入门(IF THEN ELSEIF 判断语句).docx

    TIA博途WINCC的触摸屏VB脚本入门(IF THEN ELSEIF 判断语句)

    Shell脚本if else语句小结

    代码如下: <?...if (isset($_GET[“q”])) {  search(q); } else {  //do nothing ...在sh/bash里可不能这么写,...当然,也可以写成一行(适用于终端命令提示符),像这样: 代码如下: if test $[2*3] -eq $[1+5];

Global site tag (gtag.js) - Google Analytics