参考:https://baptiste-wicht.com/posts/2012/03/cp11-concurrency-tutorial-part-2-protect-shared-data.html
1.通常情况下,当你在多个线程之间使用共享对象时,需要解决同步的问题。(synchronization)
#include <iostream> #include <thread> #include <vector> struct Counter { int value; Counter() : value(0){} void increment() { ++value; } }; int main() { Counter counter; std::vector< std::thread > threads; for(int i = 0; i < 5; ++i) { threads.push_back(std::thread([&counter](){ for(int i = 0; i < 100; i++) { counter.increment(); } })); } for(auto &thread : threads) { thread.join(); } std::cout << counter.value << std::endl; return 0; } /***** my output: 500 ********/
上述代码并非原子操作(任意时刻只有一个线程对这个共享资源进行访问)。可能引入潜在的问题,交叉存取(interleave)引起的。
/* *Thread 1 : read the value, get 0, add 1, so value = 1 *Thread 2 : read the value, get 0, add 1, so value = 1 *Thread 1 : write 1 to the field value and return 1 *Thread 2 : write 1 to the field value and return 1 */
解决方法: Semaphores, Atomic reference, Monitors, Condition codes, Compare and swap, etc.
本文主要介绍semaphore去解决该问题,实际使用的是一种特定种类的semaphore叫做mutexes.
一个mutex是一个非常简单的对象,只有一个线程可以获得一个互斥锁(the lock on the mutex at the same time)。
这个简单的特性可以让我们修正这个问题。
2.使用一个mutex确保我们的Counter 线程安全。
c++11库中mutex包含在mutex中,mutex类: std::mutex。有两个重要的方法:
mutex::lock() / unclock().lock()使一个线程获得这个锁,unclock()释放这个锁。
lock()能够实现阻塞。当lock已经被得到后线程从lock()返回。
#include <iostream> #include <thread> #include <vector> #include <mutex> struct Counter { std::mutex mutex; int value; Counter() : value(0){} void increment() { mutex.lock(); ++value; mutex.unlock(); } }; int main() { Counter counter; std::vector< std::thread > threads; for(int i = 0; i < 5; ++i) { threads.push_back(std::thread([&counter](){ for(int i = 0; i < 100; i++) { counter.increment(); } })); } for(auto &thread : threads) { thread.join(); } std::cout << counter.value << std::endl; return 0; } /*********** 能保证每次输出都为500**************/
3.Exceptions and locks
#include <iostream> #include <mutex> #include <thread> #include <vector> struct Counter { int value; Counter() : value(0) {} void increment() { ++value; } void decrement() { if(value == 0) { throw "Value cannot be less than 0"; } --value; } } struct ConcurrentCounter //wrapper { std::mutex mutex; Counter counter; void increment() { mutex.lock(); counter.increment(); mutex.unlock(); } void decrement() { mutex.lock(); try { counter.decrement(); } catch (std::string e) { mutex.unlock(); throw e; } mutex.unclock(); } };
3. Automatic management of locks.
当想要保护代码的某块区域时,存在一个好的解决方案去避免忘记释放lock.
当创建std::lock_guard 后,它自动调用和释放互斥锁lock()。因此不需要2中例子处理lock() ,unlock().
struct ConcurrentSafeCounter { std::mutex mutex; Counter counter; void increment(){ std::lock_guard<std::mutex> guard(mutex); counter.increment(); } void decrement(){ std::lock_guard<std::mutex> guard(mutex); counter.decrement(); } };