如果互斥量由成员管理,您是否需要 class/struct 的互斥量?

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

因此,如果我在线程之间通过 reference/pointer 传递变量,我应该实现互斥锁或使用 std::atomic(我知道还有其他选项)。但是,如果我改为传递一个 class 包含成员 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 作为 public 成员不会改变任何东西。您只是在 this 指针遵循方面采取了额外的方式。

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


补充:更好地思考操作:更改非原子变量是机器代码中的多个操作。 std::atomics 仅在提供原子性或看似原子性的通用操作方面有所帮助,例如使用特殊机器指令在一个原子机器步骤中比较和更改值。但是像 "change this atomic, and with that value change this other data and do this" 这样更复杂的操作,并且这些操作相互依赖,而不是你必须通过互斥锁来保护它。最好的方法是命名它,现在通过互斥锁,原子操作并将其放入如下所述的方法中。


A class 可以帮助您将 d 及其互斥锁设为私有,并且仅由读者和设置者更改和读取它,它们正在为您处理互斥锁。 IE。封装的概念。 std::atomic 在非无锁平台上正是这样做的。

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

简而言之:两种情况都是错误的(关于良好做法)

你class大致应该是这样的

#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:

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 并停止,第二个线程执行相同操作并求和,第一个线程唤醒并重写与旧值相加。

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