C++ ConcurrencyInAction:7.2.2 阻止那些讨厌的泄漏:管理无锁数据结构中的内存
C++ ConcurrencyInAction: 7.2.2 Stopping those pesky leaks: managing memory in lock-free data structures
我想了解如何在 pop() 中有多个线程保持头指针,如果两个线程在“节点 *old_head = head.load();”处保持头指针然后第一个线程将通过更新头部来完成循环,最后假设我们删除了 old_head 并且 Thread1 完成了它的工作,现在 Thread2 仍然保持 deleted old_head 并且它将进入 while 循环并且old_head和当前的head不一样,所以如果条件和设置“old_head = head;”就会转到else并再次循环并更新头部并退出循环并删除 old_head.
在上面的过程中,Thread2 永远不会取消引用已删除的节点,因为如果 head 和 old_head 的条件不相同,它将进入 else。
但是根据 C++ ConcurrencyInAction Book 的作者,我们真的需要检查持有 old_head 的其他线程吗?
From C++ ConcurrencyInAction Book:
The basic problem is that you want to free a node, but you can’t do so until you’re sure there are no other threads that still hold pointers to it.
#include <memory>
#include <atomic>
using namespace std;
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));
// background steps of compare_exchange_weak
/* if ((old_head != nullptr) && (head == old_head))
{
try
{
head = old_head->next;
return true; // loop ends
}
catch(exception &e)
{
return false; // loop forever, at the end if we are deleting old_head then we are deleting head, it makes Stack without head
}
}
else if ((old_head != nullptr) && (head != old_head))
{
old_head = head;
return false;
}
else
{
return false; // loop forever
} */
return old_head ? old_head->data : std::shared_ptr<T>();
}
};
int main()
{
return 0;
}
In the above process Thread2 never dereference the deleted node
because it will come to else if condition if head and old_head are not
same.
这不是真的。考虑这一行:
head.compare_exchange_weak(old_head, old_head->next)
这将在 compare_exchange_weak
运行之前访问 old_head->next
的值。这意味着如果 old_head
是一个悬空指针,你会得到 Undefined Behavoir.
(请注意,在这种情况下,head
将不等于 old_head
,因此 old_head->next
的值将 不被使用 ,而只是在我们知道我们不会使用它之前访问它已经导致了问题)。
此外,在 cew
的伪代码中,您似乎认为访问 old_head->next
会引发异常?在 c++
中并非如此。访问错误的指针将立即导致 Undefined Behavoir,这意味着所有赌注都已关闭。
我想了解如何在 pop() 中有多个线程保持头指针,如果两个线程在“节点 *old_head = head.load();”处保持头指针然后第一个线程将通过更新头部来完成循环,最后假设我们删除了 old_head 并且 Thread1 完成了它的工作,现在 Thread2 仍然保持 deleted old_head 并且它将进入 while 循环并且old_head和当前的head不一样,所以如果条件和设置“old_head = head;”就会转到else并再次循环并更新头部并退出循环并删除 old_head.
在上面的过程中,Thread2 永远不会取消引用已删除的节点,因为如果 head 和 old_head 的条件不相同,它将进入 else。
但是根据 C++ ConcurrencyInAction Book 的作者,我们真的需要检查持有 old_head 的其他线程吗?
From C++ ConcurrencyInAction Book: The basic problem is that you want to free a node, but you can’t do so until you’re sure there are no other threads that still hold pointers to it.
#include <memory>
#include <atomic>
using namespace std;
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));
// background steps of compare_exchange_weak
/* if ((old_head != nullptr) && (head == old_head))
{
try
{
head = old_head->next;
return true; // loop ends
}
catch(exception &e)
{
return false; // loop forever, at the end if we are deleting old_head then we are deleting head, it makes Stack without head
}
}
else if ((old_head != nullptr) && (head != old_head))
{
old_head = head;
return false;
}
else
{
return false; // loop forever
} */
return old_head ? old_head->data : std::shared_ptr<T>();
}
};
int main()
{
return 0;
}
In the above process Thread2 never dereference the deleted node because it will come to else if condition if head and old_head are not same.
这不是真的。考虑这一行:
head.compare_exchange_weak(old_head, old_head->next)
这将在 compare_exchange_weak
运行之前访问 old_head->next
的值。这意味着如果 old_head
是一个悬空指针,你会得到 Undefined Behavoir.
(请注意,在这种情况下,head
将不等于 old_head
,因此 old_head->next
的值将 不被使用 ,而只是在我们知道我们不会使用它之前访问它已经导致了问题)。
此外,在 cew
的伪代码中,您似乎认为访问 old_head->next
会引发异常?在 c++
中并非如此。访问错误的指针将立即导致 Undefined Behavoir,这意味着所有赌注都已关闭。