2017-03-17 16 views
0

我正在学习C++原子和多线程的基础知识。 根据状态(运行/休眠),我必须运行一个函数(并将状态更新为运行状态)或者什么也不做。我在C++ 11中使用atomic_compare_exchange_strong或atomic_exchange吗?

atomic_compare_exchange_strong和atomic_exchange是否有区别,如下面的代码片段所示?任何一种方法的副作用或陷阱?

std::atomic<State> state{State::sleeping}; 

for (int i = 0; i<4; i++) 
{ 
    State expected{State::sleeping}; 
    //if (std::atomic_compare_exchange_strong(&state, &expected, State::running)) 
    if (state.compare_exchange_strong(expected, State::running)) 
    { 
     cout << "running" << endl; 
     continue; 
    } 

    cout << "sleeping" << endl;    
} 

VS

std::atomic<State> state{State::sleeping}; 

for (int i = 0; i<4; i++) 
{ 
    //if (std::atomic_exchange(&state, State::running) != State::running) 
    if (state.exchange(State::running) == State::running) 
    { 
     cout << "running" << endl; 
     continue; 
    } 

    cout << "sleeping" << endl;    
} 
+0

当您阅读这两个函数的文档时发生了什么? –

+1

怀疑和困惑? – user965972

回答

4

“交流”和语义“的比较和交流”(CAS)是完全不同的,而这甚至独立的原子业务。交易是无条件的,CAS只是有条件地修改变量。

所以,当你说x.exchange(A),然后x无条件现在持有价值A。相比之下,当您说old = B; x.compare_exchange(old, A);时,如果x先前保存的值为B,则只会给出x的值,但它不会另行修改。

特别是,如果是x已经状态A,然后CAS会失败,而交换前进不管。如果你认为状态A代表需要发生的事情并且B处于空闲状态,那么CAS将允许系统在给它更多工作之前完成它所做的任何事情,而交换将仅放弃当前状态在地板上的任何状态。 (是的,您可以处理交易所的回报价值,并可能在此时恢复工作,但这通常不适用。)

当然,这两种算法都有有效的用例。交易便宜得多,应在适用时使用。以下是两个典型的例子:

  • 插入一个新的节点到在头部位置的链接列表。你首先找出当前头部,然后设置新节点指向当前头部,然后更新头部。这需要是一个CAS,因为如果当前头仍然是你想象的那样,你只想更新头。如果您在此处使用交换,则会放弃在此期间可能发生的任何其他更新。

  • 获取自旋锁。这与您的代码示例类似。你只需要知道旧值是什么,并且你不需要重复为锁字写一个新的值。因此,您可以将“锁定”值交换到该值中,只要您将之前的值“解锁”回来,就已进入关键部分。由于交换足够进行此操作,因此优于CAS。

+0

旋转锁定:我获得了旧值,然后只比较一个预先定义的值,没有潜在的副作用?我的意思是,在获取(并设置)值并评估它之间,另一个线程可能已经改变了它。 – user965972

+0

@ user965972:我们假设所有线程都执行相同的代码。所以你说'while(x.exchange(true/* = locked * /)== true/* = locked * /){}'。是的,不同的线程会将'true'写入同一个变量,但只有*读取*'false'的线程才会继续。两个线程不可能读取由同一个解锁操作导致的“错误”。 –

+0

好的。感谢您消除我的怀疑。 – user965972

相关问题