如果互斥锁是由成员管理的,那么你需要互斥锁吗?

Do you need mutex for a class/struct if mutex is managed by members?

本文关键字:成员 管理 如果      更新时间:2023-10-16

因此,如果我在线程之间通过引用/指针传递变量,我应该实现互斥或使用std::atomic(我知道还有其他选项)。但是,如果我传递一个包含std::atomic成员或具有相应互斥对象成员并希望访问成员变量的类呢?

的例子:

//class.h
#include<mutex>
#include<atomic>
class MyClass {
    Public:
        std::atomic<int> i;
        double d;
        std::mutex dmutex;
        MyClass();
    Private:
        ~MyClass();
}

//main.cpp
#includ<mutex>
#include "class.h"
void ThreadFunction (MyClass &myclass) {
    for (i = 0; i < 100; i++) {
        myclass.i++;
        myclass.dmutex.lock();
        myclass.d += 0.5;
        myclass.dmutex.unlock();
    }
    return;
}
int main () {
    MyClass commonclass;
    std::thread t_thread1 (ThreadFunction, commonclass);
    std::thread t_thread1 (ThreadFunction, commonclass);
}

//main.cpp
#includ<mutex>
#include "class.h"
void ThreadFunction (MyClass &myclass, std::mutex &mymutex) {
    for (i = 0; i < 100; i++) {
        mymutex.lock();
        myclass.i++;
        myclass.d += 0.5;
        mymutex.unlock();
    }
    return;
}
int main () {
    MyClass commonclass;
    std::mutex commonmutex;
    std::thread t_thread1 (ThreadFunction, commonclass, commonmutex);
    std::thread t_thread2 (ThreadFunction, commonclass, commonmutex);
    t_thread1.join();
    t_thread2.join();
}

让我们从只访问成员变量(我现在关心的)和成员函数的角度来讨论,假设他们修改这些成员变量并相应地处理互斥。一个比另一个更正确吗?第二个是不必要的吗?

你把事情搞混了:使用stuff作为公共成员不会改变任何东西。你只是在这个指针的差异上采取了额外的方式。

I的本质是:如果两个线程正在更改本身不是原子的数据,那么它们必须通过互斥锁进行同步。两种情况都是关于同步的。


补充:最好在操作中考虑:更改非原子变量是机器代码中的多个操作。std::atomic的帮助仅仅在于它们提供了通用的操作,这些操作是或似乎是原子的,例如,通过使用特殊的机器指令来比较和改变一个原子机器步骤中的值。但更复杂的操作,比如"改变这个原子,用那个值改变另一个数据,然后做这个",这些操作是相互依赖的,而不是你必须用互斥锁来保护它。最好的方法是把它命名为原子操作,现在用互斥锁命名,并把它放入下面描述的方法中。


一个类可以帮助你把d和它的互斥锁设为私有的,并且只由reader和setter来改变和读取它,它们正在为你处理互斥锁。即封装的概念。std::atomic正是在平台上这样做的,在那里它不是无锁的。

但是你的循环会很慢,所以std::atomic仍然是最好的。

Short:两种情况都是错误的(关于良好实践)

您的类应该大致如下所示

#include<mutex>
#include<atomic>
class MyClass {
public:
    std::atomic<int> i;
    MyClass() = default; //you could also just spare this line because you don't declare other constructors
    double d() {
        std::lock_guard<std::mutex> d_guard(dmutex);
        return this->d_;//this-> is not needed
    }
    void setd(double d) {
        std::lock_guard<std::mutex> d_guard(dmutex);
        d_ = d;
    }
    void add_to_d(double to_add){
        std::lock_guard<std::mutex> d_guard(dmutex);
        d_ += to_add;
    }
private:
    double d_;
    std::mutex dmutex;
};

void ThreadFunction (MyClass &myclass) {
    for (int i = 0; i < 100; i++) {
        myclass.i++;
        myclass.add_to_d(0.5);
    }
}   

您可能不应该将class与许多std:atomic<T>变量一起使用,因为它不能保证它们的一致性:

假设你有一个这样的类:

class A{
    std::atomic<int> a1, a2, sum;
public:
    void update(int a1, int a2){
        this->a1 = a1;
        this->a2 = a2;
        this->sum = a1 + a2;
    }
};

void update中可能存在竞争条件:一个线程修改了a1a2并被停止,第二个线程做了同样的事情并求和,第一个线程醒来并用旧值重写sum。

因此,为复杂结构提供一致性的唯一方法是使用mutex