我正在尝试学习C ++中的并发编程。
我用push(),pop(),top()和empty()方法实现了一个基本的堆栈类。
我创建了两个线程,并且两个线程都将尝试访问顶部元素并将其弹出,直到堆栈变空为止。
首先,我尝试在不使用互斥锁的情况下实现它,并且输出乱码,最后导致出现 segfault ,这是预期的,因为操作不是原子操作,因此不可避免的数据竞争
所以我试图用互斥锁实现它,并且由于未解锁互斥锁,程序甚至没有给出任何输出就挂起了。
现在,我已经正确使用了互斥锁锁定+解锁序列,我的程序正在根据需要提供正确的输出,但是此后程序挂起了-可能是由于线程仍在执行或控制未到达主线程?。
#include <thread>
#include <mutex>
#include <string>
#include <iostream>
#include <vector>
using std::cin;
using std::cout;
std::mutex mtx;
std::mutex a_mtx;
class MyStack
{
std::vector<int> stk;
public:
void push(int val) {
stk.push_back(val);
}
void pop() {
mtx.lock();
stk.pop_back();
mtx.unlock();
}
int top() const {
mtx.lock();
return stk[stk.size() - 1];
}
bool empty() const {
mtx.lock();
return stk.size() == 0;
}
};
void func(MyStack& ms, const std::string s)
{
while(!ms.empty()) {
mtx.unlock();
a_mtx.lock();
cout << s << " " << ms.top() << "\n";
a_mtx.unlock();
mtx.unlock();
ms.pop();
}
//mtx.unlock();
}
int main(int argc, char const *argv[])
{
MyStack ms;
ms.push(3);
ms.push(1);
ms.push(4);
ms.push(7);
ms.push(6);
ms.push(2);
ms.push(8);
std::string s1("from thread 1"), s2("from thread 2");
std::thread t1(func, std::ref(ms), "from thread 1");
std::thread t2(func, std::ref(ms), "from thread 2");
t1.join();
t2.join();
cout << "Done\n";
return 0;
}
我认为是因为一旦堆栈为空,我就不会解锁互斥锁。因此,当我取消注释并运行注释行时,它会输出乱码和段错误。
我不知道我在哪里犯错。这是编写线程安全的堆栈类的正确方法吗?
答案 0 :(得分:2)
一个错误是MyStack::top
和MyStack::empty
无法解锁互斥锁。
使用std::lock_guard<std::mutex>
自动解锁互斥锁并消除这种意外死锁的风险。例如:
bool empty() const {
std::lock_guard<std::mutex> lock(mtx);
return stk.empty();
}
可能还需要将互斥锁锁定在MyStack::push
中。
另一个错误是方法级别的锁定过于精细,并且empty()
后跟top()
和pop()
并不是原子的。
可能的解决方法:
class MyStack
{
std::vector<int> stk;
public:
void push(int val) {
std::lock_guard<std::mutex> lock(mtx);
stk.push_back(val);
}
bool try_pop(int* result) {
bool popped;
{
std::lock_guard<std::mutex> lock(mtx);
if((popped = !stk.empty())) {
*result = stk.back();
stk.pop_back();
}
}
return popped;
}
};
void func(MyStack& ms, const std::string& s)
{
for(int top; ms.try_pop(&top);) {
std::lock_guard<std::mutex> l(a_mtx);
cout << s << " " << top << "\n";
}
}