参考大哥http://www.sohu.com/a/248902628_100063030
在多线程环境下,i++并不安全。使用两个线程同时操作i++ 1w万次,可能得出i的值并不是2w。想要安全的使用i++, 在java上可以使用synchronized关键字, c/c++语言中可以使用pthread_mutex_lock。c的sleep函数也同样让我着迷,它可以让线程“暂停”,想不通它是怎么做到的。但感觉它们应该有共性的,抢占一个被占用的锁时是要出现“等待”的。
1. 原子操作i++以前维护过一个c/c++项目,它实现了一套的引用计数来管理对象。跟踪它在增加引用计数时调用了android_atomic_inc,android_atomic_inc又调用了android_atomic_add,android_atomic_add函数是内嵌汇编实现的:
(注:文中代码均可左右滑动)xtern ANDROID_ATOMIC_INLINE
int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr)
{
int32_t prev, tmp, status;
android_memory_barrier();
do {
__asm__ __volatile__ ("ldrex %0, [%4]\n"
"add %1, %0, %5\n"
"strex %2, %1, [%4]"
: "=&r" (prev), "=&r" (tmp),
"=&r" (status), "+m" (*ptr)
: "r" (ptr), "Ir" (increment)
: "cc");
} while (__builtin_expect(status != 0, 0));
return prev;
}
第一眼看上去,肯定会怀疑代码是不是找错了,怎么会有一个循环呢?我们慢慢分析一下,依据c/c++调用汇编参数传递格式,可以列出下表:
do {依据这个表可以把上面汇编代码转换成伪代码:
tmp = *ptr;
tmp = tmp + increment;
*ptr = tmp;
} while (status != 0);
这很好理解,关键是怎样达到原子操作(线程安全)呢,就是ldrex和strex指令的功能了,两条指令的官方文档:
主要看Operation栏。指令ldrex在取值时会在对应内存上做一个exclusive access标记;指令strex存值时有这个标记就会存值成功,返回0,没有这个标记就会存值失败,返回1。这个标记就是独占式访问标记,是针对cpu的,多核环境下也没有问题。