x86指令集中lock前缀的意义

简介: 接上篇(https://yq.aliyun.com/articles/59019),对一个变量进行加法操作,可以是3条指令,也可以是1条指令。区别在于,1条指令属于原子操作,不会被打断,那是不是只要1条指令的形式,多线程之间就不需要加锁了呢?我们先试试看。 #include

接上篇(https://yq.aliyun.com/articles/59019 ),对一个变量进行加法操作,可以是3条指令,也可以是1条指令。区别在于,1条指令属于原子操作,不会被打断,那是不是只要1条指令的形式,多线程之间就不需要加锁了呢?我们先试试看。(以下代码仅适用于x86平台,gcc编译器)

#include <stdio.h>
#include <pthread.h>

int count = 0;

void *add(void *arg)
{
    int i;

    for (i = 0; i< 10000; i++) {
        asm(
            "addl $1, %0 \n"
            : : "m"(count) : );
    }
}

int main(int argc, char *argv[])
{
    pthread_t t1, t2;

    pthread_create(&t1, NULL, add, NULL);
    pthread_create(&t2, NULL, add, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("%d\n", count);
    return 0;
}

然后运行:
images51

这是在一个多核处理器且支持对称多处理的系统上运行的结果。可见很大概率上,仍然存在同步问题。那为什么某些情况又能得到20000这个正确结果呢?鬼才知道,只能靠瞎猜。

多核多线程情况下,即便修改操作是单条指令,由于存在并发运行,也不能保证其原子性。幸好有taskset这个程序,可以设置cpu亲和性,我们先将其设置到单核试试看。

images52

嗯貌似完美。

那如果亲和性设置成两个核心呢?

images53

果然,有一定概率出现同步问题。

好了,文章要结束了,总得回到标题,类似电影要到时间了,反派再强大,也得找个方式被灭掉。lock前缀的意义在于,在多核且支持对称多处理的系统上,对于接下来的那条指令,能够保证其原子性。注意lock并不是能够往各个指令面前加的。至于如何保证了原子性,鬼才知道。

我们把add函数改写下:

  for (i = 0; i< 10000; i++) {
        asm(
            "lock \n"
            "addl $1, %0 \n"
            : : "m"(count) : );
    }

再运行几遍:

images54

好了,像是那么回事了。

目录
相关文章
|
存储 Java C语言
JVM之native关键字与PC寄存器
JVM之native关键字与PC寄存器
121 0
|
6月前
|
存储
ARM 堆栈寻址类型区分
该文介绍了堆栈的两种指向分类:向上生成型(递增堆栈)和向下生成型(递减堆栈),以及堆栈的两种数据状态:满堆栈(指针指向最后数据)和空堆栈(指针指向存放数据的位置)。满递增和满递减是在完整数据单元上操作,而空递增和空递减则允许自定义步长。文中通过图示说明了不同情况下的堆栈存储方式。
83 3
|
6月前
|
存储 程序员
【汇编】Loop指令、段前缀
【汇编】Loop指令、段前缀
492 0
【汇编】Loop指令、段前缀
|
6月前
|
存储 程序员 数据处理
【汇编】mov和add指令、确定物理地址的方法、内存分段表示法
【汇编】mov和add指令、确定物理地址的方法、内存分段表示法
717 1
【汇编】mov和add指令、确定物理地址的方法、内存分段表示法
|
6月前
|
芯片
ARM hint instruction-WFI(Wait For Interrupt)指令的一些笔记
ARM hint instruction-WFI(Wait For Interrupt)指令的一些笔记
410 0
lock和synchtonized的区别
lock和synchtonized的区别
|
存储 缓存 Java
volatile的扩展分析(2)——happens-before 与 内存屏障
volatile的扩展分析(2)——happens-before 与 内存屏障
323 0
volatile的扩展分析(2)——happens-before 与 内存屏障
|
缓存 安全 Java
volatile底层的实现原理:volatile关键字的作用、内存模型、JMM规范和CPU指令
volatile底层的实现原理:volatile关键字的作用、内存模型、JMM规范和CPU指令
163 0
KONGSBERG RCU502 设置和访问数字的存储器指令
KONGSBERG RCU502 设置和访问数字的存储器指令
95 0
KONGSBERG RCU502 设置和访问数字的存储器指令
《JUC并发编程 - 高级篇》05 -共享模型之无锁 (CAS | 原子整数 | 原子引用 | 原子数组 | 字段更新器 | 原子累加器 | Unsafe类 )(二)
《JUC并发编程 - 高级篇》05 -共享模型之无锁 (CAS | 原子整数 | 原子引用 | 原子数组 | 字段更新器 | 原子累加器 | Unsafe类 )
《JUC并发编程 - 高级篇》05 -共享模型之无锁 (CAS | 原子整数 | 原子引用 | 原子数组 | 字段更新器 | 原子累加器 | Unsafe类 )(二)