Ign*_*ant 6 c++ multithreading c++11
假设我有一个包含std::atomic_flag私有成员的类,通过getter公开.类似下面的内容(伪代码):
class Thing
{
private:
std::atomic_flag ready = ATOMIC_FLAG_INIT;
public:
isReady()
{
return ready.test_and_set();
}
}
Run Code Online (Sandbox Code Playgroud)
我天真的问题是:通过一个方法查询标志,将其转换为非原子操作,是一个非原子的函数调用(或者是?)?我应该将我的ready旗帜作为公共成员并直接查询吗?
不,它没有.该test_and_set()操作本身是原子的,所以没关系不同线程的调用栈有多深的.
为了证明这一点,请考虑atomic_flag直接"暴露"对象的基本情况:
static atomic_flag flag = ATOMIC_FLAG_INIT;
void threadMethod() {
bool wasFirst = !flag.test_and_set();
if( wasFirst ) cout << "I am thread " << this_thread::get_id() << ", I was first!" << endl;
else cout << "I am thread " << this_thread::get_id() << ", I'm the runner-up" << endl;
}
Run Code Online (Sandbox Code Playgroud)
如果两个线程进入threadMethod- 一个线程(t1)稍微在另一个(t2)之前,那么我们可以期望控制台输出如下(以相同的顺序):
I am thread t1, I was first!
I am thread t2, I'm the runner-up
Run Code Online (Sandbox Code Playgroud)
现在,如果两个线程同时进入,但是t2提前一微秒t1,但t2随后变得慢于t1写入stdout,那么输出将是:
I am thread t1, I'm the runner-up
I am thread t2, I was first!
Run Code Online (Sandbox Code Playgroud)
...所以调用test_and_set仍然是原子的,即使输出不一定是预期的顺序.
现在,如果你要包装flag另一个方法(没有内联,只是为了确定),就像这样......
__declspec(noinline)
bool wrap() {
return !flag.test_and_set();
}
void threadMethod() {
bool wasFirst = wrap();
if( wasFirst ) cout << "I am thread " << this_thread::get_id() << ", I was first!" << endl;
else cout << "I am thread " << this_thread::get_id() << ", I'm the runner-up" << endl;
}
Run Code Online (Sandbox Code Playgroud)
...然后程序的行为不会有任何不同 - 因为false或者true返回bool值test_and_set()仍然在每个线程的堆栈中.因此,包裹一个atomic_flag并没有改变它的原子性.
C++原子的原子性属性保证了操作不能在中间被破坏.也就是说,对于观察原子的第二个线程,它将观察之前test_and_set的状态或之后的状态test_and_set.这是不是可以这样的线程在一个潜行clear之间test和set部分.
但是,这仅适用于操作本身.一旦test_and_set通话完成,所有的赌注都会再次关闭.你应该总是假设执行该命令的线程在完成该指令后立即test_and_set被抢占,所以你不能假设在执行之后执行的任何指令仍然会观察到相同的状态.test_and_set
因此,添加函数调用不会让您遇到麻烦.原子变量之后的任何指令必须假设原子变量的状态在此期间可能已经改变.Atomics通过提供以特殊方式设计的接口来考虑这一点:例如,test_and_set返回测试结果,因为通过单独调用获取该信息将不再是原子的.
| 归档时间: |
|
| 查看次数: |
415 次 |
| 最近记录: |