为什么您可以从lock_free_stack中的pop()中的Atomic Compare_exchange_weak中取出的节点免费?

发布于 2025-02-11 18:52:17 字数 1409 浏览 1 评论 0原文

我正在阅读《行动》第二本书的< c ++并发。 ed>由安东尼·威廉姆斯(Anthony Williams) 第7.2.2节停止了那些讨厌的泄漏:在无锁数据中管理内存。

在本节之前,我们

template <typename T> class lock_free_stack {
private:
  struct node {
    std::shared_ptr<T> data;
    node *next;
    node(T const &data_) : data(std::make_shared<T>(data_)) {}
  };
  std::atomic<node *> head;

public:
  void push(T const &data) {
    node *const new_node = new node(data);
    new_node->next = head.load();
    while (!head.compare_exchange_weak(new_node->next, new_node));
  }
  std::shared_ptr<T> pop() {
    node *old_head = head.load();
    while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
    return old_head ? old_head->data : std::shared_ptr<T>();
  }
};

在第7.2.2节中实施了一个lock_free_stack,安东尼说:

当您第一次查看pop()时,您选择泄漏节点以避免比赛 一个线程删除节点的条件,而另一个线程仍然容纳指针 对此即将退出。

我不了解,如果两个线程同时调用pop(),则每个线程都会有不同的头,因此每个线程的old_head彼此不同,并且它们可以自由删除old_head。

我认为,无内存的裂变流行音乐()是:

  std::shared_ptr<T> pop() {
    node *old_head = head.load();
    while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
    std::shared_ptr<T> res = old_head ? old_head->data : std::shared_ptr<T>();
    delete old_head;
    return res;
  }

如何理解安东尼的话?

I'm reading the book <c++ concurrency in action 2nd. Ed> by Anthony Williams
Section 7.2.2 Stopping those pesky leaks: managing memory in lock-free data strcutures.

before this section, we implemented a lock_free_stack

template <typename T> class lock_free_stack {
private:
  struct node {
    std::shared_ptr<T> data;
    node *next;
    node(T const &data_) : data(std::make_shared<T>(data_)) {}
  };
  std::atomic<node *> head;

public:
  void push(T const &data) {
    node *const new_node = new node(data);
    new_node->next = head.load();
    while (!head.compare_exchange_weak(new_node->next, new_node));
  }
  std::shared_ptr<T> pop() {
    node *old_head = head.load();
    while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
    return old_head ? old_head->data : std::shared_ptr<T>();
  }
};

in section 7.2.2, anthony said:

When you first looked at pop(), you opted to leak nodes in order to avoid the race
condition where one thread deletes a node while another thread still holds a pointer
to it that it’s about to dereference.

I don't understand, if two threads calling pop() concurrently, each thread will get different head, so old_head of each thread differes from each other, and they are free to delete old_head.

In my opinion, the no-memory-leaks pop() would be:

  std::shared_ptr<T> pop() {
    node *old_head = head.load();
    while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
    std::shared_ptr<T> res = old_head ? old_head->data : std::shared_ptr<T>();
    delete old_head;
    return res;
  }

How to understand what anthony says ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你的呼吸 2025-02-18 18:52:17

比赛在这里

  while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
                                                           ^^^^^^^^^^^^^^

可能的一场比赛是:

  1. 线程1呼叫head.load(),在地址x
  2. call.load.load()中
  3. 2
  4. thread 检索节点在old_head
  5. thread 2上删除地址x线程的
  6. 节点1尝试读取已删除的old_head-&gt; next,在其自己的调用之前,已删除了compare_exchange_weak_weak

The race is here

  while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
                                                           ^^^^^^^^^^^^^^

One possible race is:

  1. Thread 1 calls head.load(), retrieving a node at address X
  2. Thread 2 calls head.load(), retrieving a node at address X
  3. Thread 2 does the exchange
  4. Thread 2 performs its own operations on old_head
  5. Thread 2 deletes node at address X
  6. Thread 1 attempts to read old_head->next, which has been deleted, just prior to its own call to compare_exchange_weak
空袭的梦i 2025-02-18 18:52:17

让我们假设2个线程Enter pop同时却被中断了,因此中断了这样的中断:

Thread 1                                 Thread 2
node *old_head = head.load();            node *old_head = head.load();
while (old_head &&
   !head.compare_exchange_weak(old_head,
                       old_head->next));
std::shared_ptr<T> res =
     old_head ? old_head->data
              : std::shared_ptr<T>();
delete old_head;
                                         while (old_head &&
                                            !head.compare_exchange_weak(old_head,
                                                                old_head->next));
                                      

两个线程都以相同的old_head = head.load();开始。然后线程1删除对象,然后在该线程2访问old_head--&gt; Next。这是免费的。

Lets assume 2 threads enter pop at the same time but thread 2 gets interrupted for a bit like this:

Thread 1                                 Thread 2
node *old_head = head.load();            node *old_head = head.load();
while (old_head &&
   !head.compare_exchange_weak(old_head,
                       old_head->next));
std::shared_ptr<T> res =
     old_head ? old_head->data
              : std::shared_ptr<T>();
delete old_head;
                                         while (old_head &&
                                            !head.compare_exchange_weak(old_head,
                                                                old_head->next));
                                      

Both threads start with the same old_head = head.load();. Then thread 1 deletes the object and after that thread 2 accesses old_head->next. That's access after free.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文