打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C++11多线程——lock详解
C++11多线程——lock详解
标签: C++11多线程lock详解学习
2015-04-17 11:37 2458人阅读 评论(0)  举报
 分类:
多线程(3) 
 C/C++(15) 
目录(?)[+]
C++11提供了两种管理锁的类
std::lock_guard:与mutex RAII相关,方便线程对互斥量上锁
std::unique_lock:   与mutex RAII相关,方便线程对互斥量上锁,相比std::lock_guard提供了更好的上锁和解锁控制
一 lock_guard详解
lock_guard是一个模板类:template<classMutex>class lock_guard;
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
[cpp] view plain copy
#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
[cpp] view plain copy
#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::unique_lock简介
std::unique_lock与std::lock_guard一样用来管理锁对象(在抛出异常之前上锁的对象使用unique_lock管理的锁对象也可正常解锁,可一定程度上避免死锁),其与std::lock_guard类似,但是给程序员提供了足够的灵活度。
在构造时,unique_lock对相关需要一个Mutex锁对象作为其参数,新创建的unique_lock对象负责传入的Mutex锁对象的上锁和解锁操作
unique_lock与lock_guard一样,不负责管理Mutex锁对象生命周期
std::unique_lock构造函数
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
[cpp] view plain copy
#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;
}
std::unique(移动)赋值操作
函数原型
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
[cpp] view plain copy
#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异常。
[cpp] view plain copy
// 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
[cpp] view plain copy
// 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
[cpp] view plain copy
// 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
[cpp] view plain copy
// 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只释放所有权,不解锁)
[cpp] view plain copy
// 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;
[cpp] view plain copy
// 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功能相同。
[cpp] view plain copy
// 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锁对象的指针,这里不多做介绍
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C++11 并发指南三(Lock 详解)
C 多线程的常见用法
C++11 并发指南五(std::condition
如何实现C 中的多线程编程
如何使用C++共享指针std::shared_ptr?
C++11中的智能指针
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服