首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >理解compare_exchange_weak

理解compare_exchange_weak
EN

Stack Overflow用户
提问于 2020-10-18 12:07:33
回答 2查看 664关注 0票数 0

下面的代码是来自compare_exchange_weak的https://www.cplusplus.com/reference/atomic/atomic/compare_exchange_weak/示例

我不明白在compare_exchange_weak while循环中哪种操作是可以接受的。

在本例中,只要oldHead返回false,它们就会将newNode->next值设置为compare_exchange指针。我不明白这怎么总是有效的。

如果另一个线程在同一个循环中,并且它成功地在我们设置oldHead指针的时间和我们的线程上的compare_exchange成功之间更改它,那么会发生什么呢?那么我们的newNode中就会有错误的头部指针。我不明白为什么这不可能。

例如,如果我在设置了->next值之后在循环中放置了一个睡眠(5),或者做了一些长时间的计算,这会成功吗?

标记为“这里安全的是什么”的循环是我不明白的部分。

代码语言:javascript
复制
// atomic::compare_exchange_weak example:
#include <iostream>       // std::cout
#include <atomic>         // std::atomic
#include <thread>         // std::thread
#include <vector>         // std::vector

// a simple global linked list:
struct Node { int value; Node* next; };
std::atomic<Node*> list_head (nullptr);

void append (int val) {     // append an element to the list
  Node* oldHead = list_head;
  Node* newNode = new Node {val,oldHead};

  // what follows is equivalent to: list_head = newNode, but in a thread-safe way:
  while (!list_head.compare_exchange_weak(oldHead,newNode))

    newNode->next = oldHead;                 // <-- WHAT IS SAFE TO DO HERE?

    // WHAT IF I DO SOMETHING STUPID LIKE THIS?
    //sleep(5);

    // OR LIKE THIS:
    //some_long_calculation();    

  }

int main ()
{
  // spawn 10 threads to fill the linked list:
  std::vector<std::thread> threads;
  for (int i=0; i<10; ++i) threads.push_back(std::thread(append,i));
  for (auto& th : threads) th.join();

  // print contents:
  for (Node* it = list_head; it!=nullptr; it=it->next)
    std::cout << ' ' << it->value;
  std::cout << '\n';

  // cleanup:
  Node* it; while (it=list_head) {list_head=it->next; delete it;}

  return 0;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-10-18 15:52:18

基本上,你可以在循环中做你喜欢做的任何事情。只有当要更新的变量的值等于第一个提供的参数时,compare_exchange (弱变量和强变量)才会成功。在您的示例中,list_head.compare_exchange_weak(oldHead,newNode)只能在list_head == oldHead成功之后才能成功。因此,如果在循环中有睡眠或一些长时间的计算,那么其他线程很可能会更快地更新list_head,因此您的compare_exchange操作可能会失败。需要注意的是,第一个参数是通过引用传递的。如果比较失败,则使用新值更新给定参数。

oldHead存储在newNode->next中是完全安全的,因为如果后续compare_exchange失败,则oldHead被更新,新值再次存储在newNode->next中,然后我们再次尝试(然后,再一次,.)直到compare_exchange最终成功。在这种情况下,可以保证存储在newNode->next中的值与我们刚才在list_head - voilà中替换的值相匹配,我们已经成功地将节点插入到列表中。

代码语言:javascript
复制
  // what follows is equivalent to:
  //   newNode->next = list_head;
  //   list_head = newNode;
  // but in a thread-safe way:
  while (!list_head.compare_exchange_weak(oldHead,newNode)) {
    // if we are here, the compare_exchange_weak has failed, but
    // oldHead has been updated with the current value from list_head
    newNode->next = oldHead;
  }
  // if we are here, the compare_exchange_weak was successful, i.e., the value
  // in list_head (oldHead) did not change between our assignment to newNode->next
  // and the subsequent (successful) compare_exchange.

弱版本和强版本的不同之处在于,compare_exchange_weak可能会虚假地失败。也就是说,在比较结果为真的情况下,它甚至可能失败。之所以会出现弱版本,是因为在使用LL/SC实现比较交换的体系结构上,compare_exchange_weak比强版本便宜。通常,如果您有一个重试循环,如您的示例中,您应该更喜欢compare_exchange_weak,因为如果您真的有一个(罕见的)虚假失败,我们只需执行另一个重试。

票数 3
EN

Stack Overflow用户

发布于 2020-10-18 15:50:21

compare_exchange_(weak/strong)是为原子数据的一般修改而设计的。不完全确定两者之间有什么区别--我认为,当原子不释放并且多次无法获得锁时,情况会有所不同。

当不能锁定数据时,如何执行一般的修改?想想看。计算原子数据的一些通用函数,但同时对数据进行了修改。这是不可能避免的,因为一个人无法锁定原子。

因此,人们提出的是检查数据是否从函数启动时开始更改,如果没有,则用新值替换它。否则,它只是将预期值替换为当前值。因此,通常只需重试计算函数,直到数据在两次计算之间保持不变,或者计算变得不必要为止。

示例:您有一个整数x,您希望用max(x,y)更新原子整数y,而另一个线程可能使用不同的x值进行相同的更新。

因此,从理论上讲,您可以在compare_exchange_(weak/strong)中加入任何函数,但是如果计算时间很长,那么重试的代价太大,这将是不切实际的。在这种情况下使用互斥,这样就不需要重试了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64413109

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档