Dekker算法在多核处理器下的失效

Dekker algorithm是一种著名的并发编程的算法,Dekker算法的核心部分是一组对称的代码来访问一组共享变量,使得两个线程不可能同时进入临界区(只要cpu内存模型是遵循顺序一致性的),从而达到线程同步的目的。以下是该算法的一种实现:

static volatile int flag1 = 0;
static volatile int flag2 = 0;
static volatile int turn = 1;
static volatile int gSharedCounter = 0;

void dekker1( ) {
flag1 = 1;
turn = 2;
while((flag2 == 1) && (turn == 2)) ;
// Critical section
gSharedCounter++;
// Let the other task run
flag1 = 0;
}

void dekker2(void) {
flag2 = 1;
turn = 1;
while((flag1 == 1) && (turn == 1)) ;
// critical section
gSharedCounter++;
// leave critical section
flag2 = 0;
}

该实现的关键在于while((flag2 == 1) && (turn == 2))与while((flag1 == 1) && (turn == 1))永远不可能同时成立,从而可以互斥的访问临界区,并且由于条件不可能同时成立,也不会导致死锁。以下面的例子进行测试:

int gLoopCount;
void *task1(void *arg) {
int i;
printf("Starting task1n");
for(i=gLoopCount;i>0;i--) {
dekker1();
}
}
void *task2(void *arg) {
int i;
printf("Starting task2n");
for(i=gLoopCount;i>0;i--) {
dekker2();
}
}

在单核处理器下,以多线程的方式执行这段代码,无论运行多少次,程序也不会出错。因为单核CPU的内存模型是遵循顺序一致性(Sequential Consistency)的,Sequential Consistency模型(后面简称SC),简单说它其实就是我们印象中多线程程序应该有的执行顺序。但是,SC最大的问题是性能太低了,因为CPU/编译器完全没有必要严格按代码规定的顺序(program order)来执行每一条指令。学过体系结构的同学应该知道不管是编译器也好CPU也好,他们最擅长做的事情就是帮你做乱序优化。在串行时代这些乱序优化对程序员来说都是透明的,封装好了的,你不用关心它们到底给你乱序成啥样了,因为它们会保证优化后的程序的运行结果跟你写程序时预期的结果是一模一样的。但是进入多核时代之后,CPU和编译器还会继续做那些串行时代的优化,更重要的是这些优化还会打破你多线程程序的SC模型语义,从而使得多线程程序的实际运行结果与我们所期待的运行结果不一致!
拿X86来说,它的多核内存模型没有严格执行SC,即属于weak ordering(或者叫relax ordering?)。它唯一允许的乱序优化是可以把对不同地址的load操作提到store之前去(即把store x->load y乱序优化成load y -> store x)。而store x -> store y、load x -> load y,以及load y -> store x不允许交换执行顺序。
因此,对于弱内存一致性的机器来说,该算法有可能会失效,因为对于按什么顺序来更新flag1和flag2是没有限制的,特别是不能保证在dekker1中对flag2的读操作发生在dekker2对flag1和turn的写操作之后。

参考:剖析为什么在多核多线程程序中要慎用volatile关键字?

时间: 2024-12-19 02:42:22

Dekker算法在多核处理器下的失效的相关文章

[转帖]AMD、英特尔为何争相走向胶水多核处理器?真相在此

AMD.英特尔为何争相走向胶水多核处理器?真相在此 胶水多核到底好不好?这个事不是简单一句话能说明的,今天的超能课堂里我们就来聊聊MCM胶水多核技术的过去及未来. 作者:孟宪瑞来源:超能网|2018-11-21 15:25 收藏 分享 从2017年到现在,不到两年时间里X86处理器行业的变化要比过去五年都要大,标志性事件就是AMD重返高性能处理器市场,除了在桌面处理器力推8核16线程处理器之外,在服务器市场上还推出了32核64线程处理器,前不久更推出了7nm的64核128线程"罗马"处

[py]你真的了解多核处理器吗? 了解多线程

你真的了解多核处理器吗? 1.双核≠双性能 多核不一定会使你的手机或电脑速度更快,但它将提高你的PC的整体性能,这是一个有所不同的细微的技术特色.多核处理器的性能提升并不是简单CPU核心的倍数,因为受到两(多)个核之间共享资源的拖累.比如双核性能只是单核的1.4-1.8倍,实际情况取决于具体的应用. 一个核拼命干活,拼命转啊转啊,其他核在看,最后温度不断上升然后蓝屏了.... 上图是个笑话,不过你可以用任务管理器可以看到并不是每个核的负载时均匀分配的. 这是为什么一个核转啊转啊,最后蓝屏的原因.

【CSWS2014 Summer School】互联网广告中的匹配和排序算法-蒋龙(下)

[CSWS2014 Summer School]互联网广告中的匹配和排序算法-蒋龙(上) Fig19,用到了矩阵,这个我没有听太明白,蒋博士也没有详细说明.不过可以明确的一点就是,我们常说的K-means.聚类等技术都是有实际应用价值的,而且我们日常就在使用. Fig20,这个定向广告,就更加接近推荐系统了,根据用户的特征,来投放特定的广告,这样回报率会高一点. Fig21,这里定向方式,也就是一些特征,可以利用用户的人口属性.地理位置.上网的行为等等.这个重定向,其实大家每天都在接受着重定向广

ARM多核处理器启动过程分析

说明: 该流程图按照代码执行时间顺序划分为4部分: 1.     Bootloader在图片上半部,最先启动: 2.     Kernel在图片下半部,由bootloader引导启动: 3.CPU0执行流程在图片左半部,bootloader代码会进行判断,先行启动CPU0: 4.  Secondary CPUs在图片右半部,由CPU唤醒 具体启动流程如下: 1.     在bootloader启动时,会判断执行代码的是否为CPU0,如果不是,则执行wfe等待CPU0发出sev指令唤醒.如果是CP

多核处理器的由来

多核处理器的由来-在21世纪初,能量强power wall无法再想以前一样增加CPU的时钟频率(因为功耗太大)-所以使用了多个小处理器核core取代单个大处理器来提高性能-每个完整的处理器都可以独立的执行程序 P = fCv^2f = 时钟频率C = 电容v = 电压 电容C类似于总面积,所以只要总面积不变,能耗就不会变,然后按照摩尔定律指数型的下降,性能就会提升

018给定二叉查找树的一个结点, 写一个算法查找它的“下一个”结点“(keep it up)

给定二叉查找树的一个结点, 写一个算法查找它的"下一个"结点(即中序遍历后它的后继结点), 其中每个结点都有指向其父亲的链接. 这个题本质就是线索化二叉树时找后继结点的题.找后继结点存在两种情况: 1 如果当前结点有右孩子,则后继结点为右孩子的最左结点 2 如果没有右孩子, A 当前结点为父结点的左孩子,则父结点就是后继结点 B 当前结点为父结点的右孩子,则向父结点找,直到当前结点不是父结点的右孩子终止,此时 父节点就是后继结点 代码: struct TreeNode { int da

IE6/IE7下margin-bottom失效兼容解决办法及双倍边距问题

(从已经死了一次又一次终于挂掉的百度空间人工抢救出来的,发表日期 2014-04-08) 一.IE6/IE7下margin-bottom失效兼容解决办法 1.用padding-bottom代替:2.在父标签中加入overflow:hidden:或zoom:100%示例代码: 不正常显示 ul{} li{ list-style:none; float:left; margin:10px;} 1. 你会发现左边10px 变成20px了, 解决办法:在li的CSS中加入:display:inline

进程互斥(二)Dekker算法

假设P.Q两个进程需要互斥进入某一个临界区. Dekker算法的基本思想是:首先看P.Q是否有进入临界区的意愿,(1)如果没有进程想进临界区,自然不用做任何事情,(2)如果只有一个进程想进临界区,就让他进入临界区,(3)如果两个进程同时都想进临界区,那么再看轮到谁进临界区了,如果刚好轮到P,P就进入临界区,Q等待,P退出临界区后Q再进入:同样,如果刚好轮到Q,Q就进入临界区,P等待,Q退出临界区后P再进入. Dekker算法的具体实施方法: (1)设置两个布尔变量pturn,qturn.ptur

linux多核cpu下的负载查看

linux下使用top命令或uptime命令 单核cpu下,负载超过0.7即意味着瓶颈,多核cpu下按核数*0.7计算负载 如2核,1.4可能即意味着负载较吃力了 查看核数 grep 'model name' /proc/cpuinfo | wc -l