使用Mutex会使程序挂起

时间:2018-09-13 16:02:18

标签: c++ multithreading concurrency mutex

我正在尝试学习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;
}

我认为是因为一旦堆栈为空,我就不会解锁互斥锁。因此,当我取消注释并运行注释行时,它会输出乱码和段错误。

我不知道我在哪里犯错。这是编写线程安全的堆栈类的正确方法吗?

1 个答案:

答案 0 :(得分:2)

一个错误是MyStack::topMyStack::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";
    }
}