原子性的保证

前言

有时困扰的原子性问题

操作原子性

x86中
内存读写操作是原子的
可以使用LOCK锁定总线
cache coherency保证cache原子性

x=y 多线程不安全 不原子
x++ 多线程不安全 不原子
++x lock inc 原子
x=1 多线程安全 原子

Bit field(位域)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct foo {
int flag : 1;
int counter : 15;
};
struct foo my_foo;
/* ... */
/* in thread 1 */
pthread_mutex_lock(&my_mutex_for_flag);
my_foo.flag = !my_foo.flag;
pthread_mutex_unlock(&my_mutex_for_flag);
/* in thread 2 */
pthread_mutex_lock(&my_mutex_for_counter);
++my_foo.counter;
pthread_mutex_unlock(&my_mutex_for_counter);

即使分别加锁,仍然不能保证线程安全,如上。

因为这俩变量本应该毫不相关分别锁,因此可以同时读取赋值。
因为读取和赋值都是最小一个字节一起操作的,使用mov读取后用位操作修改再用mov赋值,此时修改了几个位的变量。

因此这种相邻位域是线程不安全的!!!!

解决用同一个锁或者使用bool来单独记

实际使用的锁

linux中的同步机制、信号量、互斥量等,基于CPU提供的原子操作、关中断、锁内存总线的机制

单处理器关中断即可保证一定区域原子性。
多处理器可以使用test and set操作。这是忙等的…tsl实现有汇编支持,实现赋值1返回旧值的操作,原子。

比如基于tls实现自旋锁-忙等

1
2
3
4
function Lock(boolean *lock) {
while (test_and_set (lock) == 1)
;
}

因此可保证你所担心的多线程同时访问锁同时读取状态的问题不会发生,因为cpu的几种原子操作保证了设置锁并返回旧值的操作是原子的!!!