线程间共享数据的问题
1 int g_nResource = 0; 2 void thread_entry() 3 { 4 for (int i = 0; i < 10000000; ++i) 5 g_nResource++; 6 } 7 8 int main() 9 { 10 thread th1(thread_entry); 11 thread th2(thread_entry); 12 th1.join(); 13 th2.join(); 14 cout << g_nResource << endl; 15 return 0; 16 }
10161838
1 int g_nResource = 0; 2 std::mutex _mutex; //使用互斥 3 void thread_entry() 4 { 5 _mutex.lock(); //加锁 6 for (int i = 0; i < 10000000; ++i) 7 g_nResource++; 8 _mutex.unlock(); //解锁 9 }
输出:
20000000
用互斥保护共享数据
在C++中使用互斥
std::mutex
1 mutex(); 2 3 //不支持拷贝构造,也不支持移动构造(有定义拷贝,则无移动) 4 mutex(const mutex&) = delete; 5 mutex& operator=(const mutex&) = delete;
1 void lock();
1 void unlock();
bool try_lock();
1 int g_nResource = 0; 2 std::mutex _mutex; 3 void thread_entry() 4 { 5 while (1) 6 { 7 if (_mutex.try_lock()) 8 { 9 cout << this_thread::get_id() << " get lock\n"; 10 for (int i = 0; i < 10000000; ++i) 11 g_nResource++; 12 _mutex.unlock(); 13 return; 14 } 15 else 16 { 17 cout << this_thread::get_id() << " no get lock\n"; 18 this_thread::sleep_for(std::chrono::milliseconds(500)); 19 } 20 } 21 } 22 23 int main() 24 { 25 thread th1(thread_entry); 26 thread th2(thread_entry); 27 th1.join(); 28 th2.join(); 29 cout << "Result = " << g_nResource << endl; 30 }
131988 get lock 136260 no get lock 136260 get lock Result = 20000000
std::lock_guard
1 template <class _Mutex> 2 class _NODISCARD lock_guard { // class with destructor that unlocks a mutex 3 public: 4 using mutex_type = _Mutex; 5 6 explicit lock_guard(_Mutex& _Mtx) : _MyMutex(_Mtx) { // construct and lock 7 _MyMutex.lock(); 8 } 9 10 lock_guard(_Mutex& _Mtx, adopt_lock_t) : _MyMutex(_Mtx) {} // construct but don't lock 11 12 ~lock_guard() noexcept { 13 _MyMutex.unlock(); 14 } 15 16 lock_guard(const lock_guard&) = delete; 17 lock_guard& operator=(const lock_guard&) = delete; 18 19 private: 20 _Mutex& _MyMutex; 21 };
1 int g_nResource = 0; 2 std::mutex _mutex; 3 void thread_entry() 4 { 5 lock_guard<mutex> lock(_mutex); 6 for (int i = 0; i < 10000000; ++i) 7 g_nResource++; 8 }
锁的策略标签
1 std::lock(lhs._mutex, rhs._mutex); //对lhs、rhs上锁 2 std::lock_guard<mutex> lock_a(lhs._mutex, std::adopt_lock); //不再上锁 3 std::lock_guard<mutex> lock_b(rhs._mutex, std::adopt_lock); //不再上锁
组织和编排代码以保护共享数据
1 class SomeData 2 { 3 public: 4 void DoSomething() { cout << "do something\n"; } 5 }; 6 7 class Operator 8 { 9 public: 10 void process(std::function<void(SomeData&)> func) 11 { 12 std::lock_guard<mutex> lock(_mutex); 13 func(data); //数据外溢 14 } 15 16 private: 17 SomeData data; 18 mutex _mutex; 19 }; 20 21 void GetDataPtr(SomeData** pPtr, SomeData& data) 22 { 23 *pPtr = &data; 24 } 25 26 int main() 27 { 28 Operator opt; 29 SomeData* pUnprotected = nullptr; 30 auto abk = [pUnprotected](SomeData& data) mutable 31 { 32 pUnprotected = &data; 33 }; 34 opt.process(abk); 35 pUnprotected->DoSomething(); //以无锁形式访问本应该受到保护的数据 36 }
发现接口固有的条件竞争
1 void func() 2 { 3 stack<int> s; 4 if (!s.empty()) 5 { 6 int nValue = s.top(); 7 s.pop(); 8 do_something(nValue); 9 } 10 }
消除返回值导致的条件竞争的方法
template<typename T> class myStack { public: myStack(); ~myStack(); void pop(T& data); //传入引用接收数据 }; int main() { myStack<DataRes> s; DataRes result; s.pop(result); }
死锁问题
1 class A 2 { 3 public: 4 A(int nValue) : m_nValue(nValue) {} 5 friend void Swap(A& lhs, A& rhs) 6 { 7 if (&lhs == &rhs) return; 8 lock_guard<mutex> lock_a(lhs._mutex); 9 lock_guard<mutex> lock_b(rhs._mutex); 10 std::swap(lhs.m_nValue, rhs.m_nValue); 11 } 12 private: 13 int m_nValue; 14 mutex _mutex; 15 }; 16 17 void func(A& lhs, A& rhs) 18 { 19 Swap(lhs, rhs); 20 } 21 22 int main() 23 { 24 A a1(10); 25 A a2(20); 26 thread th1(func, std::ref(a1), std::ref(a2)); //传入参数顺序不同 27 thread th2(func, std::ref(a2), std::ref(a1)); //传入参数顺序不同 28 th1.join(); 29 th2.join(); 30 }
std::lock()函数
1 class A 2 { 3 public: 4 A(int nValue) : m_nValue(nValue) {} 5 6 friend void Swap(A& lhs, A& rhs) 7 { 8 if (&lhs == &rhs) return; 9 std::lock(lhs._mutex, rhs._mutex); 10 std::lock_guard<mutex> lock_a(lhs._mutex, std::adopt_lock); //已经上锁,不再加锁 11 std::lock_guard<mutex> lock_b(rhs._mutex, std::adopt_lock); //已经上锁,不再加锁 12 std::swap(lhs.m_nValue, rhs.m_nValue); 13 } 14 15 private: 16 int m_nValue; 17 mutex _mutex; 18 };
std::scoped_lock类
1 scoped_lock<mutex, mutex> lock(lhs._mutex, rhs._mutex);
1 scoped_lock lock(lhs._mutex, rhs._mutex);
防范死锁的补充准则
准则1:避免嵌套锁
准则2:一旦持锁,就须避免调用由用户提供的程序接口
准则3:依次从固定顺序获取锁
准则4:按层级加锁
运用std::unique_lock类灵活加锁
构造函数
unique_lock(); unique_lock(_Mutex&); //构造并调用lock上锁 ~unique_lock(); //析构并调用unlock解锁 //构造,_Mtx已经被锁,构造函数不在调用lock unique_lock(_Mutex&, adopt_lock_t); //构造,但不对_Mtx上锁,需后续手动调用 unique_lock(_Mutex&, defer_lock_t) //构造,尝试获取锁,不会造成阻塞 unique_lock(_Mutex&, try_to_lock_t) //构造 + try_lock_shared_for unique_lock(_Mutex&, const chrono::duration<_Rep, _Period>&); //构造 + try_lock_shared_until unique_lock(_Mutex&, const chrono::time_point<_Clock, _Duration>&); unique_lock(unique_lock&& _Other); //移动构造 //若占有则解锁互斥,并取得另一者的所有权 unique_lock& operator=(unique_lock&& _Other); //无拷贝构造 unique_lock(const unique_lock&) = delete; unique_lock& operator=(const unique_lock&) = delete;
成员函数
//锁定关联互斥 void lock(); //解锁关联互斥 void unlock(); //尝试锁定关联互斥,若互斥不可用则返回 bool try_lock(); //试图锁定关联的可定时锁定 (TimedLockable) 互斥,若互斥在给定时长中不可用则返回 bool try_lock_for(const chrono::duration<_Rep, _Period>&); //尝试锁定关联可定时锁定 (TimedLockable) 互斥,若抵达指定时间点互斥仍不可用则返回 bool try_lock_until(const chrono::time_point<_Clock, _Duration>&); //与另一 std::unique_lock 交换状态 void swap(unique_lock& _Other); //将关联互斥解关联而不解锁它 _Mutex* release(); //测试是否占有其关联互斥 bool owns_lock(); //同owns_lock operator bool(); //返回指向关联互斥的指针 _Mutex* mutex();
在不同的作用域之间转移互斥归属权
1 std::mutex _Mtx; 2 3 void PrepareData() {} 4 5 void DoSomething() {} 6 7 std::unique_lock<std::mutex> get_lock() 8 { 9 std::unique_lock<std::mutex> lock(_Mtx); 10 PrepareData(); 11 return lock; 12 } 13 14 void ProcessData() 15 { 16 std::unique_lock<std::mutex> lock(get_lock()); 17 DoSomething(); 18 }
按适合的粒度加锁
1 std::mutex _Mtx; 2 bool GetAndProcessData() 3 { 4 std::unique_lock<std::mutex> lock(_Mtx); 5 DataResource data = GetData(); 6 lock.unlock(); 7 bool bResult = WirteToFile(data); //非常耗时 8 lock.lock(); 9 SaveResult(bResult); 10 return bResult; 11 }
1 class Y 2 { 3 private: 4 int some_detail; 5 mutable std::mutex m; 6 int get_detail() const 7 { 8 std::lock_guard<std::mutex> lock_a(m); 9 return some_detail; 10 } 11 public: 12 Y(int sd):some_detail(sd){} 13 friend bool operator==(Y const& lhs, Y const& rhs) 14 { 15 if(&lhs==&rhs) 16 return true; 17 int const lhs_value=lhs.get_detail(); 18 int const rhs_value=rhs.get_detail(); 19 return lhs_value==rhs_value; ⇽--- ④ 20 } 21 };
保护共享数据的其他工具
在初始化过程中保护共享数据
std::call_once()函数与std::once_flag
1 template <class _Fn, class... _Args> 2 void(call_once)(once_flag& _Once, _Fn&& _Fx, _Args&&... _Ax);
1 class Singleton 2 { 3 public: 4 static Singleton* Ins() 5 { 6 std::call_once(_flag, []() { 7 _ins = new Singleton; 8 }); 9 return _ins; 10 } 11 12 Singleton(const Singleton&) = delete; 13 Singleton& operator=(const Singleton&) = delete; 14 15 protected: 16 Singleton() { std::cout << "constructor" << std::endl; } 17 ~Singleton() { std::cout << "destructor" << std::endl; } //必须声明为私有,否则返回指针将可析构 18 19 private: 20 struct Deleter 21 { 22 ~Deleter() { 23 delete _ins; 24 _ins = nullptr; 25 } 26 }; 27 static Deleter _deleter; 28 static Singleton* _ins; 29 static std::once_flag _flag; 30 }; 31 32 Singleton::Deleter Singleton::_deleter; 33 Singleton* Singleton::_ins = nullptr; 34 std::once_flag Singleton::_flag;
Magic Static特性
1 class Singleton 2 { 3 public: 4 static Singleton& Ins() 5 { 6 static Singleton _ins; 7 return _ins; 8 } 9 10 Singleton(const Singleton&) = delete; 11 Singleton& operator=(const Singleton&) = delete; 12 13 protected: 14 Singleton() { std::cout << "constructor" << std::endl; } 15 ~Singleton() { std::cout << "destructor" << std::endl; } 16 };
保护甚少更新的数据结构
std::shared_mutex
shared_mutex(); //构造互斥 ~shared_mutex(); //析构互斥 //无拷贝 shared_mutex(const shared_mutex&) = delete; shared_mutex& operator=(const shared_mutex&) = delete;
void lock(); //锁定互斥,若互斥不可用则阻塞 void unlock(); //解锁互斥 void try_lock(); //尝试锁定互斥,若互斥不可用则返回
void lock_shared(); //为共享所有权锁定互斥,若互斥不可用则阻塞 bool try_lock_shared(); //尝试为共享所有权锁定互斥,若互斥不可用则返回 void unlock_shared(); //解锁共享所有权互斥
1 std::shared_mutex _Mtx; 2 void func() 3 { 4 _Mtx.lock_shared(); 5 cout << " thread Id = " << this_thread::get_id() << " do something!\n"; 6 _Mtx.unlock_shared(); 7 } 8 9 int main() 10 { 11 _Mtx.lock_shared(); //使用共享锁锁住 12 thread th1(func); 13 thread th2(func); 14 th1.join(); 15 th2.join(); 16 _Mtx.unlock_shared(); 17 }
std::shared_timed_mutex
shared_timed_mutex(); ~shared_timed_mutex(); shared_timed_mutex(const shared_timed_mutex&) = delete; shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
void lock(); //锁定互斥,若互斥不可用则阻塞 void unlock(); //解锁互斥 bool try_lock(); //尝试锁定互斥,若互斥不可用则返回 //尝试锁定互斥,若互斥在指定的时限时期中不可用则返回 bool try_lock_for(const chrono::duration<_Rep, _Period>&); //尝试锁定互斥,若直至抵达指定时间点互斥不可用则返回 bool try_lock_until(const chrono::time_point<_Clock, _Duration>&)
void lock_shared(); //为共享所有权锁定互斥,若互斥不可用则阻塞 bool try_lock_shared(); //尝试为共享所有权锁定互斥,若互斥不可用则返回 void unlock_shared(); //解锁互斥(共享所有权) //尝试为共享所有权锁定互斥,若互斥在指定的时限时期中不可用则返回 bool try_lock_shared_for(const chrono::duration<_Rep, _Period>&); //尝试为共享所有权锁定互斥,若直至抵达指定时间点互斥不可用则返回 bool try_lock_shared_until(const chrono::time_point<_Clock, _Duration>&);
std::shared_lock
shared_lock(); shared_lock(mutex_type&); //构造并调用lock_shared上锁 ~shared_lock(); //析构并调用unlock_shared解锁 //构造,但不对_Mtx上锁,需后续手动调用 shared_lock(mutex_type&, defer_lock_t) //构造,尝试获取锁,不会造成阻塞 shared_lock(mutex_type&, try_to_lock_t) //构造,_Mtx已经被锁,构造函数不在调用lock shared_lock(mutex_type&, adopt_lock_t) //构造 + try_lock_shared_for shared_lock(mutex_type&, const chrono::duration<_Rep, _Period>&) //构造 + try_lock_shared_until shared_lock(mutex_type&, const chrono::time_point<_Clock, _Duration>&) shared_lock(shared_lock&&); //移动构造 shared_lock& operator=(shared_lock&&); //移动赋值,会先解锁
//锁定关联的互斥 void lock(); //尝试锁定关联的互斥 bool try_lock(); //解锁关联的互斥 void unlock(); //尝试锁定关联的互斥,以指定时长 try_lock_for(const chrono::duration<_Rep, _Period>&); //尝试锁定关联的互斥,直至指定的时间点 bool try_lock_until(const chrono::time_point<_Clock, _Duration>&); //解除关联 mutex 而不解锁 mutex_type* release(); //测试锁是否占有其关联的互斥 bool owns_lock(); //同owns_lock operator bool(); //返回指向关联的互斥的指针 mutex_type* mutex(); //与另一 shared_lock 交换数据成员 void swap(shared_lock& _Right)
1 class A 2 { 3 public: 4 A& operator=(const A& other) 5 { 6 //上独占锁(写操作) 7 unique_lock<shared_mutex> lhs(_Mtx, defer_lock); 8 9 //上共享锁(读操作) 10 shared_lock<shared_mutex> rhs(other._Mtx, defer_lock); 11 12 //上锁 13 lock(lhs, rhs); 14 15 to_do_assignment(); //赋值操作 16 return *this; 17 } 18 private: 19 mutable std::shared_mutex _Mtx; 20 };
递归加锁
文章来源:https://www.toymoban.com/news/detail-709759.html
Copyright
文章来源地址https://www.toymoban.com/news/detail-709759.html
到了这里,关于c++并发编程实战-第3章 在线程间共享数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!