如何保证一个成员函数在另一个成员函数之前获得 mutex?

1 天前
 LuckyPocketWatch

假设有个类的两个成员函数,需要在不同的线程里执行,这两个成员函数需要写入同一个文件,因此需要使用 mutex 确保这两个函数不会同时写,代码类似这样

class Widget{
public:
    void funA();
    void funnB();
private:
    std::mutex mutex;
};
void Widget::funA(){
    {
        std::unique_lock(mutex);
        //operate A;
    }
}
void Widget::funB(){
    {
        std::unique_lock(mutex);
        //operate B;
    }
}

然后设计两个函数来封装这两个成员函数,类似

void execA(Widget* w){
    w->funcA();
};

最后程序里,生成一个 Widget 的对象,需要在两个线程里执行这两个函数,即

Widget* w{};
std::jthread threadA(execA, &w);  //在一个线程里执行 A 函数
std::jthread threadB(execB, &w);  //在另一个线程里执行 B 函数

在这种情况下,大多数式成员函数 funcA()先执行,但多次尝试后,有几次式 funcB()先获得 mutex 。

在下列三个前置条件下

  1. 使用 c++20 标准,并可以使用 vs2022 最新版支持的 c++23 标准
  2. 不该用 std::thread
  3. 不使用 std::this_thread::sleep_for()对 B 进行睡眠

我向问下,有没有办法确保 A 一定在 B 之前获得 mutex ?

561 次点击
所在节点    C++
7 条回复
LcDraven
1 天前
用信号量控制两个线程的同步关系
billlee
1 天前
你的需求是 threadB 在 threadA 后执行,这个不是 mutex 能解决的问题。请使用条件变量
LcDraven
1 天前
@billlee 他的场景用条件变量不是把简单问题复杂化了吗?用信号量最优吧我觉得,或者再用一个互斥锁也可以同步线程的同步关系,甚至用全局变量加互斥锁也可以。但是信号量是最简单最专业的吧
GeruzoniAnsasu
1 天前
https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange

spinlock 的典型场景呗

B 线程读 flag, A 线程写 flag
billlee
1 天前
@LcDraven 嗯,对。Semaphore 更简单一点,刚才没想到
jujusama
1 天前
在 Widget 的构造内将 mutex.lock(),在 function A 内使用 std::unique_lock(mutex, std::adopt_lock);
A1st0n
7 小时 49 分钟前
使用条件变量:
```
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>

class Widget {
public:
void funcA() {
std::unique_lock lock(mutex_);
std::cout << "funcA starts\n";
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟操作
std::cout << "funcA ends\n";

// 标记 A 已完成,通知等待的线程
funcA_done_ = true;
cv_.notify_one();
}

void funcB() {
// 等待 funcA 完成
std::unique_lock lock(cv_mutex_);
cv_.wait(lock, [this] { return funcA_done_; });

std::unique_lock file_lock(mutex_);
std::cout << "funcB starts\n";
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟操作
std::cout << "funcB ends\n";
}

private:
std::mutex mutex_; // 用于文件操作的互斥锁
std::mutex cv_mutex_; // 用于条件变量的互斥锁
std::condition_variable cv_; // 条件变量
bool funcA_done_ = false; // 标志 funcA 是否已完成
};

// 封装函数
void execA(Widget* w) {
w->funcA();
}

void execB(Widget* w) {
w->funcB();
}

int main() {
Widget w;

// 使用 std::jthread
std::jthread threadA(execA, &w); // 在一个线程里执行 A 函数
std::jthread threadB(execB, &w); // 在另一个线程里执行 B 函数

return 0;
}
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1090860

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX