lock_guard对象通常用来管理某个锁(Lock)对象,与Mutex RALL相关,方便线程对互斥量上锁,在其声明周期内,它管理的锁一直保持上锁状态;在其声明周期结束之后,它锁管理的锁会被自动释放(即用构造函数对锁对象上锁,析构函数对锁对象解锁)
模板参数Mutex代表几种基本的BasicLockable类型分别为:std::mutex, std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex以及 std::unique_lock, (BasicLockable类型只需满足两种操作,lock和unlock。Lockable类型在BasicLockable的基础上增加了try_lock操作。TimedLockable类型在Lockable的基础上又增加了try_lock_for和try_lock_until操作。)
注意:lock_guard对象并不负责管理Mutex对象的生命周期,它只是简化了mutex的上锁和解锁操作,再其生命周期内,它锁管理的锁对象会一直保持上锁状态;声明周期结束之后,它锁管理的锁对象会被自动解锁。其最大的优点是安全易用(在出现异常时依旧可以正确解锁,一定程度上避免了死锁)。
lock_guard构造函数如下所示
locking-constructor (a) exlicit lock_guard(mutex_type& m);
adopting-constructor (b) lock_guard(mutex_type&m,adopt_lock_ttag);
copy(deleted) -constructor (c) lock_guard(const lock_guard&) = delete;
a locking-constructor
lock_guard对象管理Mutex对象m,并在构造时对m上锁
b adopting-constructor初始化
lock_guard对象管理Mutex对象m,与locking初始化不同的是,Mutex对象以被当前线程锁住。(将mutex对象用adopting::lock_guard管理,最终在调用lock_guard析构函数时,m锁对象会被自动解锁)
c copy-constructor
lock_guard的拷贝构造与移动构造均被禁用
locking-constructor examples
#include <iostream> #include <thread> #include <mutex> #include <stdexcept> std::mutex mtx; void printEven(int x) { if (0 == x % 2) { std::cout << x << " is even\n"; } else { throw (std::logic_error("not even\n")); } } void printThreadID(int id) { try { std::lock_guard<std::mutex>lck(mtx); printEven(id); } catch (std::logic_error&e) { //std::cout << e.what() << std::endl; std::cout << "[exception caught]\n"; } } int main(int argc, _TCHAR* argv[]) { std::thread threads[10]; for (int i = 0; i < 10;++i) { threads[i] = std::thread(printThreadID, i + 1); } for (auto &th : threads) { th.join(); } return 0; }
在voidprintThreadID(int id)中,首先捕捉voidprintEven(int x)中抛出的异常,在try块内,首先对mtx锁对象构造lock_guard对象lck(此语句之后,mtx锁对象由lck管理),即在try块作用域内(也就是lck对象生命周期内),mtx锁对象被上锁,在lck生命周期结束时mtx锁对象自动解锁(在抛出异常时,依旧可正确解锁)。
adopting-constructor example
#include <iostream> #include <thread> #include <mutex> std::mutex mtx; void printThreadID(int id) { mtx.lock(); std::lock_guard<std::mutex>lck(mtx, std::adopt_lock); std::cout << "thread # " << id << '\n'; } void testAdoptingConstructor() { std::thread threads[10]; for (int i = 0; i < 10; ++i) { threads[i] = std::thread(printThreadID, i + 1); } for (auto& th : threads) { th.join(); } } int main(int argc, _TCHAR* argv[]) { testAdoptingConstructor(); return 0; }
在void printThreadID(int id)中,首先对mtx上锁,之后调用lock_guard的adopting-constructor来构造lck,用lck管理mtx锁对象。(注意,std::adopt_lock表明当前线程已获得锁)
std::unique_lock与std::lock_guard一样用来管理锁对象(在抛出异常之前上锁的对象使用unique_lock管理的锁对象也可正常解锁,可一定程度上避免死锁),其与std::lock_guard类似,但是给程序员提供了足够的灵活度。
在构造时,unique_lock对相关需要一个Mutex锁对象作为其参数,新创建的unique_lock对象负责传入的Mutex锁对象的上锁和解锁操作
unique_lock与lock_guard一样,不负责管理Mutex锁对象生命周期
default(a) unique_lock()noexcept;
locking(b) explicit unique_lock(mutex_type&m);
try_locking(c) unique_lock(mutex_type&m,try_to_lock_t tag);
deferred(d) unique_lock(mutex_type&m, defer_lock_t tag)noexcept;
adopting(e) unique_lock(mutex_type&m,adopt_lock_ttag);
lookingfor(f) template <classRep,class Period>unique_lock(mutex_type&m,const chrono::duration<Rep,Period>&rel_time);
locking until(g) template<class Clock,classDuration>unique_lock(mutex_type& m,const chrono::time_point<Clock,Duration>&abs_time);
copy[delete](h) unique_lock(const unique_lock&) = delete;
move(i) unique_lock(unique_lock&&x);
- a default_constructor
新创建的unique_lock对象不管理任何Mutex锁对象
- b locking constructor
新创建的unique_lock对象管理锁对象m,并调用m.lock()对m上锁,若另外某个unique_lock管理了该Mutex锁对象m,则当前线程会被阻塞。
- c try_locking constructor
新创建的unique_lock对象管理锁对象m,调用m.try_lock()尝试上锁,若上锁失败,并不会阻塞当前线程。
- d deferred constructor
新建的unique_lock对象管理Mutex锁对象m,初始化时并不锁住Mutex锁对象m, m是一个没有被当前线程锁住的Mutex对象呢
- e adopting constructor
新创建的unique_lock管理Mutex锁对象m,m是已经被当前线程锁住的Mutex对象,并且新创建的unique_lock对象拥有对锁的所有权
- flocking for constructor
新创建的unique_lock管理Mutex锁对象m,并试图通过m.try_lock_for(read_time)来锁住Mutex对象一段时间
- g locking until constructor
新创建的unique_lock管理Mutex锁对象m,并试图通过m.try_lock_until(abs_time)在某个时间点之前锁住Mutex锁对象m
- h copy constructor —— deleted
unique_lock对象不能被拷贝构造
- i move constructor
新创建的unique_lock对象拥有x所管理的Mutex锁对象的所有权。而此时x对象如默认构造函数锁创建的unique_lock对象一样,不管理任何Mutex锁对象
- 总结:由b、e创建的unique_lock对象通常拥有Mutex对象的锁,通过 a、d创建的unique_lock对象不会拥有锁。通过 c、f、g创建的unique_lock在lock成功时获得锁
- std::unique_lock constructor examples
#include <iostream> #include <thread> #include <mutex> std::mutex mtxOne,mtxTwo; void taskA() { std::lock(mtxOne,mtxTwo); std::unique_lock<std::mutex>lck1(mtxOne, std::adopt_lock); std::unique_lock<std::mutex>lck2(mtxTwo, std::adopt_lock); std::cout << "taskA\n"; } void taskB() { std::unique_lock<std::mutex>lck1, lck2; lck1 = std::unique_lock<std::mutex>(mtxOne, std::defer_lock); lck2 = std::unique_lock<std::mutex>(mtxTwo, std::defer_lock); std::lock(lck1, lck2); std::cout << "taskB\n"; } int main(int argc, _TCHAR* argv[]) { std::thread th1(taskA); std::thread th2(taskB); th1.join(); th2.join(); return 0; }
- 函数原型
move(a) unique_lock& operator=(unique_lock&& x)noexcept
copy[deleted](b) unique_lock& operator=(unique_lock&) = delete
- 详解:
move assignment:移动赋值之后,有x所管理的锁对象及其及其状态被新的std::unique_lock取代。如果被赋值std::unique_lock对象之前已经获得了其他Mutex对象的锁,则在移动赋值之前调用unlock成员函数释放其锁占用的锁。而x如默认构造函数构造的std::unique_lock对象一样,不在管理任何Mutex锁对象。
- std::unique_lock move assignment examples
#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock std::mutex mtx; // mutex for critical section void print_fifty(char c) { std::unique_lock<std::mutex> lck; // default-constructed lck = std::unique_lock<std::mutex>(mtx); // move-assigned for (int i = 0; i<50; ++i) { std::cout << c; } std::cout << '\n'; } int main() { std::thread th1(print_fifty, '*'); std::thread th2(print_fifty, '$'); th1.join(); th2.join(); return 0; }
std::unique_lock member functions
- 上锁/解锁:lock、try_lock、try_lock_for、try_lock_until、unlock
- 获取属性:owns_lock(返回unique_lock对象是否获得锁,若获得锁则返回true,否则返回false),operator bool(与owns_lock一样,大多用于条件判断),mutex返回当前unique_lock对象所管理的Mutex对象的指针
- 修改操作:移动赋值,swap(与另一个unique_lock对象交换他们所管理的Mutex锁对象的所有权),release(释放unique_lock管理的Mutex对象的所有权,并返回之前管理的Mutex对象的指针)
std::unique_lock::lock详解:
对std::unique_lock所管理的锁对象上锁,若在调用lock时其他线程以对该Mutex对象已被其他线程锁住,当前线程被阻塞直至它获得了锁。改函数返回时,代表std::unique_lock对象已经拥有它所管理的Mutex对象的锁,如果上锁失败,则抛出system_error异常。
// unique_lock::lock/unlock #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock, std::defer_lock std::mutex mtx; // mutex for critical section void print_thread_id(int id) { std::unique_lock<std::mutex> lck(mtx, std::defer_lock); // critical section (exclusive access to std::cout signaled by locking lck): lck.lock(); std::cout << "thread #" << id << '\n'; lck.unlock(); } int main() { std::thread threads[10]; // spawn 10 threads: for (int i = 0; i<10; ++i) threads[i] = std::thread(print_thread_id, i + 1); for (auto& th : threads) th.join(); return 0; }
std::unique_lock::try_lock
对std::unique_lock所管理的Mutex对象上锁,如果上锁成功则返回true,否则返回false
// unique_lock::try_lock example #include <iostream> // std::cout #include <vector> // std::vector #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock, std::defer_lock std::mutex mtx; // mutex for critical section void print_star() { std::unique_lock<std::mutex> lck(mtx, std::defer_lock); // print '*' if successfully locked, 'x' otherwise: if (lck.try_lock()) std::cout << '*'; else std::cout << 'x'; } int main() { std::vector<std::thread> threads; for (int i = 0; i<500; ++i) threads.emplace_back(print_star); for (auto& x : threads) x.join(); return 0; }
- std::unique_lock::try_lock_for
对std::unique_lock所管理的Mutex对象上锁,如果上锁成功则返回true,否则返回false
// unique_lock::try_lock_for example #include <iostream> // std::cout #include <chrono> // std::chrono::milliseconds #include <thread> // std::thread #include <mutex> // std::timed_mutex, std::unique_lock, std::defer_lock std::timed_mutex mtx; void fireworks() { std::unique_lock<std::timed_mutex> lck(mtx, std::defer_lock); // waiting to get a lock: each thread prints "-" every 200ms: while (!lck.try_lock_for(std::chrono::milliseconds(200))) { std::cout << "-"; } // got a lock! - wait for 1s, then this thread prints "*" std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "*\n"; } int main() { std::thread threads[10]; // spawn 10 threads: for (int i = 0; i<10; ++i) threads[i] = std::thread(fireworks); for (auto& th : threads) th.join(); return 0; }
std::unique_lock::try_lock_until
对std::unique_lock所管理的Mutex对象上锁,如果上锁成功则返回true,否则返回false
// timed_mutex::try_lock_until example #include <iostream> // std::cout #include <chrono> // std::chrono::system_clock #include <thread> // std::thread #include <mutex> // std::timed_mutex #include <ctime> // std::time_t, std::tm, std::localtime, std::mktime std::timed_mutex cinderella; // gets time_point for next midnight: std::chrono::time_point<std::chrono::system_clock> midnight() { using std::chrono::system_clock; std::time_t tt = system_clock::to_time_t(system_clock::now()); struct std::tm * ptm = std::localtime(&tt); ++ptm->tm_mday; ptm->tm_hour = 0; ptm->tm_min = 0; ptm->tm_sec = 0; return system_clock::from_time_t(mktime(ptm)); } void carriage() { std::unique_lock<std::timed_mutex> lck(cinderella, std::defer_lock); if (lck.try_lock_until(midnight())) { std::cout << "ride back home on carriage\n"; lck.unlock(); } else std::cout << "carriage reverts to pumpkin\n"; } void ball() { std::unique_lock<std::timed_mutex> lck(cinderella, std::defer_lock); lck.lock(); std::cout << "at the ball...\n"; } int main() { std::thread th1(ball); std::thread th2(carriage); th1.join(); th2.join(); return 0; }
std::unique_lock::release
释放std::unique_lock所管理对象的所有权,并返回指向其管理Mutex对象的指针(注意,std::unique_lock::release只释放所有权,不解锁)
// unique_lock::release example #include <iostream> // std::cout #include <vector> // std::vector #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock std::mutex mtx; int count = 0; void print_count_and_unlock(std::mutex* p_mtx) { std::cout << "count: " << count << '\n'; p_mtx->unlock(); } void task() { std::unique_lock<std::mutex> lck(mtx); ++count; print_count_and_unlock(lck.release()); } int main() { std::vector<std::thread> threads; for (int i = 0; i<10; ++i) threads.emplace_back(task); for (auto& x : threads) x.join(); return 0; }
std::unique_lock::owns_lock
返回std::unique_lock对象是否获得了它所管理的Mutex锁对象的锁,若std::unique_lock已获得Mutex对象的锁,则返回true,否则返回false;
// unique_lock::operator= example #include <iostream> // std::cout #include <vector> // std::vector #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock, std::try_to_lock std::mutex mtx; // mutex for critical section void print_star() { std::unique_lock<std::mutex> lck(mtx, std::try_to_lock); // print '*' if successfully locked, 'x' otherwise: if (lck.owns_lock()) std::cout << '*'; else std::cout << 'x'; } int main() { std::vector<std::thread> threads; for (int i = 0; i<500; ++i) threads.emplace_back(print_star); for (auto& x : threads) x.join(); return 0; }
std::unique_lock::operator bool
std::unique_lock::operator bool与std::unique_lock::owns_lock功能相同。
// unique_lock::operator bool #include <iostream> // std::cout #include <vector> // std::vector #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock, std::try_to_lock std::mutex mtx; // mutex for critical section void print_star() { std::unique_lock<std::mutex> lck(mtx, std::try_to_lock); // print '*' if successfully locked, 'x' otherwise: if (lck) std::cout << '*'; else std::cout << 'x'; } int main() { std::vector<std::thread> threads; for (int i = 0; i<500; ++i) threads.emplace_back(print_star); for (auto& x : threads) x.join(); return 0; }
- std::unique_lock::mutex
此成员函数只是简单的返回指向std::unique_lock管理的Mutex锁对象的指针,这里不多做介绍
原文地址:http://blog.csdn.net/u013507368/article/details/45094959