![](/img/trans.png)
[英]Why does nvmlDeviceGetTemperature only work in debug mode?
[英]Why does program only work with debugging mode?
我试图了解无锁编程,并写了一个无锁堆栈:
template <typename T>
class LockFreeStack
{
struct Node
{
std::shared_ptr<T> data;
Node* next;
explicit Node(const T& _data)
: data(std::make_shared<T>(_data)), next(nullptr)
{}
};
std::atomic<Node*> head;
public:
void push(const T& data)
{
auto n{new Node(data)};
n->next = head.load();
while (!head.compare_exchange_weak(n->next, n))
;
}
std::shared_ptr<T> pop(void)
{
auto 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>{};
}
};
以及两个用于推送/弹出操作的线程:
static LockFreeStack<int> global_stack;
和main
功能:
int main(void)
{
std::srand(std::time(nullptr));
std::thread pushing_thread([](void) {
for (size_t i{}; i < MAX_LENGTH; ++i)
{
const auto v{std::rand() % 10000};
global_stack.push(v);
std::cout << "\e[41mPoping: " << v << "\e[m" << std::endl;
}
});
std::thread poping_thread([](void) {
for (size_t i{}; i < MAX_LENGTH; ++i)
{
if (auto v{global_stack.pop()}; v)
{
std::cout << "\e[42mPushing: " << *v << "\e[m" << std::endl;
}
}
});
pushing_thread.join();
poping_thread.join();
}
该程序仅在调试模式下运行pushing_thread
,但是当我使用调试器运行该程序时,它会按预期运行两个线程,或者如果我在线程之间等待片刻:
std::thread pushing_thread(...);
std::this_thread::sleep_for(1s);
std::thread poping_thread(...);
它工作正常。 那么当我们用调试器运行程序时会发生什么呢?
GCC 9.3
。-std=c++2a -lpthread -Wall
。ArchLinux with linux-5.5.13
。这个逻辑有问题:
while (old_head && head.compare_exchange_weak(old_head, old_head->next))
;
如果 old_head 不为空并且交换成功,则再试一次!
您的实现存在所谓的 ABA 问题。 考虑以下场景:
A->B->C
(即头指向 A)A
的地址)和A
的下一个指针( B
),但在它可以执行 CAS 之前,它被中断了。A
,然后B
,然后压入A
,即堆栈现在看起来像这样: A->C
A
更新到B
-> 您的堆栈已损坏 - 看起来像这样: B->C
- 但B
当前正在被线程 2 使用。有几种可能的解决方案可以避免 ABA 问题,例如标记指针或并发内存回收方案。
更新:
标记指针只是一个用版本计数器扩展的指针,每次更新指针时版本标记都会增加。 您可以使用 DWCAS (Double-Width-CAS) 来更新具有单独版本字段的结构,或者您可以将版本标记压缩到指针的高位。 并非所有架构都提供 DWCAS 指令(x86 提供),如果高位未使用,则取决于操作系统(在 Windows 和 Linux 上,通常可以使用最高 16 位)。
关于内存回收方案的主题,我可以向您推荐我的论文: C++ 中无锁数据结构的有效内存回收
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.