如果互斥量由成员管理,您是否需要 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::atomic
s 仅在提供原子性或看似原子性的通用操作方面有所帮助,例如使用特殊机器指令在一个原子机器步骤中比较和更改值。但是像 "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
中可能存在竞争条件:一个线程已更改 a1
和 a2
并停止,第二个线程执行相同操作并求和,第一个线程唤醒并重写与旧值相加。
因此,为复杂结构提供一致性的唯一方法是使用 mutex
。
因此,如果我在线程之间通过 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::atomic
s 仅在提供原子性或看似原子性的通用操作方面有所帮助,例如使用特殊机器指令在一个原子机器步骤中比较和更改值。但是像 "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
中可能存在竞争条件:一个线程已更改 a1
和 a2
并停止,第二个线程执行相同操作并求和,第一个线程唤醒并重写与旧值相加。
因此,为复杂结构提供一致性的唯一方法是使用 mutex
。