接上篇(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;
}
然后运行:
这是在一个多核处理器且支持对称多处理的系统上运行的结果。可见很大概率上,仍然存在同步问题。那为什么某些情况又能得到20000这个正确结果呢?鬼才知道,只能靠瞎猜。
多核多线程情况下,即便修改操作是单条指令,由于存在并发运行,也不能保证其原子性。幸好有taskset这个程序,可以设置cpu亲和性,我们先将其设置到单核试试看。
嗯貌似完美。
那如果亲和性设置成两个核心呢?
果然,有一定概率出现同步问题。
好了,文章要结束了,总得回到标题,类似电影要到时间了,反派再强大,也得找个方式被灭掉。lock前缀的意义在于,在多核且支持对称多处理的系统上,对于接下来的那条指令,能够保证其原子性。注意lock并不是能够往各个指令面前加的。至于如何保证了原子性,鬼才知道。
我们把add函数改写下:
for (i = 0; i< 10000; i++) {
asm(
"lock \n"
"addl $1, %0 \n"
: : "m"(count) : );
}
再运行几遍:
好了,像是那么回事了。