C++中将监视器类成员方法作为线程参数的报错解决方案
首先,你遇到的invalid use of non-static member function错误,核心原因是非静态成员函数必须绑定到类的实例才能被调用——这类函数默认会接收一个隐式的this指针作为第一个参数,而std::thread需要明确知道这个this指向哪个对象。
一、正确创建调用非静态成员函数的线程
你完全不需要把成员函数改成静态,只需要在创建std::thread时,把监视器实例的指针(或引用)作为第二个参数传递进去即可:
int main() { monitor m; // 方式1:传递实例指针 std::thread t1(&monitor::m_add, &m, 1); // 方式2:用std::ref传递实例引用 std::thread t2(&monitor::m_add, std::ref(m), 2); t1.join(); t2.join(); return 0; }
这样std::thread就能正确把&m(或std::ref(m))作为this指针,绑定到m_add函数上执行。
二、优化你的监视器锁设计(避免死锁+正确封装)
你当前用多个互斥锁的方式存在很大风险:比如m_remove同时锁定三个互斥锁,m_add锁定两个,这种细粒度锁很容易因为锁的顺序不一致导致死锁。而且std::list的操作(push_back/remove/find)都是针对整个容器的,细粒度锁没有实际意义,反而增加复杂度。
更合理的方案是:
- 把
std::list<int>移到monitor类的私有成员中,彻底封装,禁止外部直接访问 - 用单个互斥锁保护所有对list的操作,保证同一时间只有一个线程能访问list
修改后的完整代码如下:
#include <list> #include <thread> #include <mutex> #include <algorithm> class monitor{ public: void m_add(int x){ std::lock_guard<std::mutex> lock(mtx); // RAII自动加锁/解锁,避免手动操作失误 l.push_back(x); } void m_remove(int x){ std::lock_guard<std::mutex> lock(mtx); l.remove(x); } bool m_contains(int x){ std::lock_guard<std::mutex> lock(mtx); return std::find(l.begin(), l.end(), x) != l.end(); } private: std::list<int> l; // 将list移入类内,彻底封装保护 std::mutex mtx; // 单个互斥锁保护所有list操作,简洁且安全 }; int main() { monitor m; std::thread t_add(&monitor::m_add, &m, 5); std::thread t_check(&monitor::m_contains, &m, 5); t_add.join(); t_check.join(); return 0; }
这里用std::lock_guard实现RAII式的锁管理,不需要手动调用lock()和unlock(),能避免因为异常或代码分支导致的锁未释放问题,更安全可靠。
三、为什么不能直接用thread t(m.m_add, 1);?
非静态成员函数的签名本质上是void (monitor::*)(int),而不是普通的void(*)(int)。当你写m.m_add时,并没有自动绑定this指针,std::thread无法识别这个未绑定的成员函数,因此会报错。必须显式传递实例的指针/引用,让std::thread把它作为this参数传递给成员函数。
内容的提问来源于stack exchange,提问作者André Winston

