返回信息流如何在不使用"锁"机制下,实现一段程序的原子执行?
这是一条镜像帖。来源:北邮人论坛 / cpp / #86829同步于 2015/4/28
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
linux下的C++编程问题请教
jgn93520
2015/4/28镜像同步10 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
如果使用C++11的话,atomic头文件里有很多你想要的东西。但是memory model真的很不好学,建议想清楚究竟要做什么再用这些原子内存操作。
http://en.cppreference.com/w/cpp/atomic
我还不支持c++11
【 在 nuanyangyang 的大作中提到: 】
: 如果使用C++11的话,atomic头文件里有很多你想要的东西。但是memory model真的很不好学,建议想清楚究竟要做什么再用这些原子内存操作。
: http://en.cppreference.com/w/cpp/atomic
【 在 vnebula 的大作中提到: 】
: 在用户空间基本不可能实现一段代码的原子执行,一句还可以讨论
: 有内核空间是可以的,禁中断、禁内核抢占,不使用会休眠的代码
可以的。原子执行其实不需要操作系统的协助,但关键是处理器的支持。操作系统能做的只是让一个线程睡觉,或者醒来,以避免忙等待。
比如,下面这段程序用spinlock实现互斥锁:
#include <atomic>
using namespace std;
class Lock {
atomic<int> v; // 这个原子类型很重要。对它的赋值、读取都是原子的。
public:
Lock(): v(0) {}
void lock() {
// exchange是原子的,将内存里的值和参数1互换,中间保证不会有别的线程插入别的操作。
// 不断尝试交换,直到发现我把原来的0变成1了为止。0表示原先没有锁住,1表示上锁了。
while(v.exchange(1) == 0) {
// do nothing
}
// 忙等待结束以后,肯定是上锁状态,而且必须是当前线程上的锁。
}
void unlock() {
v = 0; // 由于v是atomic<int>,对它的赋值是原子的。
// 这就够了。一旦设置成0,其他一直在忙等待的线程只有一个会把它由0变成1,
// 其它的是由1变成1。所以只有一个别的线程可以得到锁。
}
};
Lock myLock;
int data;
void publisher(int newData) {
myLock.lock();
data = newData;
myLock.unlock();
}
int reader() {
myLock.lock();
int oldData = data;
myLock.unlock();
return oldData;
}
【 在 vnebula 的大作中提到: 】
: 在用户空间基本不可能实现一段代码的原子执行,一句还可以讨论
: 有内核空间是可以的,禁中断、禁内核抢占,不使用会休眠的代码
其实根本的原子内存操作机制是CPU提供的。
比如x86上,只要是不跨越cache line的界限,mov指令的读写都是原子的。
除此之外,还提供一些可以一步完成的read-modify-write操作,包括:
xchg
cmpxchg
以及加LOCK前缀的add, and, or, xor等等
另一些处理器(比如arm和power)提供load link/store conditional机制。可以想象成给单个内存地址上锁(有硬件协助)。
C/C++语言里的stdatomic.h和atomic库只是利用了这些指令而已。
你理解的原子执行仅仅是一种互斥访问而已,互斥访问不是原子执行。你代码里面可能仅仅只有对atomic变量的赋值是原子的
我说的原子执行是这段代码肯定会一直执行,直到结束,甚至不会被硬件中断,更不会被操作系统调度出去,你的代码里面lock函数里面,lock之后进程都有可能被其它进程抢占,被硬件中断的概率就更大了
【 在 nuanyangyang 的大作中提到: 】
:
: 可以的。原子执行其实不需要操作系统的协助,但关键是处理器的支持。操作系统能做的只是让一个线程睡觉,或者醒来,以避免忙等待。
: 比如,下面这段程序用spinlock实现互斥锁:
: ...................
【 在 vnebula 的大作中提到: 】
: 你理解的原子执行仅仅是一种互斥访问而已,互斥访问不是原子执行。你代码里面可能仅仅只有对atomic变量的赋值是原子的
: 我说的原子执行是这段代码肯定会一直执行,直到结束,甚至不会被硬件中断,更不会被操作系统调度出去
那应该称为“不可中断”,而不是“原子”。如果没有同步,另一个线程在读这种“不可中断”的线程进行的操作的时候,是可以读到写了一半的结果的,这不能称为是“原子”的。
在驱动编程程里对原子上下文有具体的定义,能不能被中断我记不清了,但肯定不能休眠,不能调度,你的代码所在的进程完全有可能被其它进程抢占
【 在 nuanyangyang 的大作中提到: 】
:
: 那应该称为“不可中断”,而不是“原子”。如果没有同步,另一个线程在读这种“不可中断”的线程进行的操作的时候,是可以读到写了一半的结果的,这不能称为是“原子”的。
【 在 vnebula 的大作中提到: 】
: 在驱动编程程里对原子上下文有具体的定义,能不能被中断我记不清了,但肯定不能休眠,不能调度,你的代码所在的进程完全有可能被其它进程抢占
嗯。是的。在用户态只能做到这一步了,否则就要内核干涉了。但即使被抢占了,在正确使用锁或者原子内存操作的时候,别的线程也是无法读到写了一半的结果的。