如何正确使用互斥锁作为线程中成员函数的参数?

时间:2019-04-15 22:10:11

标签: c++ multithreading c++17 mutex

我的问题是我不知道如何正确使用互斥锁。我了解它在理论上是如何工作的,但是我不知道为什么它在我的代码中不起作用。我想,如果我在var上使用互斥锁,它将被阻塞,直到被解锁。尽管如此,看来我仍然有一场数据竞赛。

我试图在类中定义一个互斥体和一个互斥体,它们通过引用传递给我。不知何故,这没用。


class test {
public:
    void dosmth(std::mutex &a);
    int getT(){return t;};

private:
    int t = 0;
};


void test::dosmth(std::mutex &a) {
    for(;;){
        a.lock();
        t++;
        if(t==1000){
            t=0;
        }
        a.unlock();
    }
}

int main() {
    test te;
    std::mutex help;
    std::thread t(&test::dosmth, std::addressof(te), std::ref(help));
    for(;;){
        for (int i = 0; i <te.getT() ; ++i) {
            std::cout<<te.getT()<<std::endl;
        }
    }
}

结果应该是我得到了一些输出,所以现在我可以使用了。

3 个答案:

答案 0 :(得分:2)

正如Michael所提到的,您应该同步读取器和写入器,以避免未定义的行为。代替传递mutex作为参数,一种常见的模式是使mutex成为对象(te)的成员,每次都锁定和解锁(最好使用lock_gaurd您可以输入成员函数(修改对象的内部状态),而不是手动锁定和解锁。这是一些伪代码:

class Foo{
    std::mutex m; // reader and writer are sync'ed on the same mutex
    int data_to_sync;
public:
    int read(){
        lock_gaurd<mutex> lg(m); //RAII lock
        return data_to_sync;
        //automagically released upon exit
    }
    void write(){
        lock_gaurd<mutex> lg(m);
        data_to_sync++;
    }
};

答案 1 :(得分:1)

仅当互斥锁用于调节所有 critical sections的代码输入时,互斥锁才能保证互斥。就您而言,您的第二个线程在主线程读取同一对象的值时修改了对象te.t的值。但是,只有一个线程正在使用互斥锁来保护对te.t的访问。对象te.t不是原子的。因此,您将面临数据争用,从而导致行为未定义[intro.races]/21

您还必须在主for(;;)循环中锁定和解锁互斥锁,例如:

    for(;;){
        help.lock();
        for (int i = 0; i <te.getT() ; ++i) {
            std::cout<<te.getT()<<std::endl;
        }
        help.unlock();
    }

或更佳,使用std::lock_guard

    for(;;){
        std::lock_guard lock(help);
        for (int i = 0; i <te.getT() ; ++i) {
            std::cout<<te.getT()<<std::endl;
        }
    }

答案 2 :(得分:0)

  

我想如果我在var上使用互斥锁...

您不要在“变量上”使用互斥量。锁定互斥锁可以防止其他线程同时锁定同一互斥锁,但是 not 不会阻止其他线程访问任何特定变量。

如果您希望使用互斥锁来保护一个变量(或更常见的是多个变量),以防止多个线程同时访问该变量,则由您决定您不会编写任何代码来访问变量而不先锁定互斥锁。