C++ folly库解读(三)Synchronized —— 比std::lock_guard/std::unique_lock更易用、功能更强大的同步机制
2021/6/18 20:27:20
本文主要是介绍C++ folly库解读(三)Synchronized —— 比std::lock_guard/std::unique_lock更易用、功能更强大的同步机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录
- 传统同步方案的缺点
- folly/Synchronized.h 简单使用
- Synchronized
的模板参数 - withLock()/withRLock()/withWLock() —— 更易用的加锁方式
- 升级锁
- ulock()和 withULockPtr()
- Timed Locking
- Synchronized 与 std::condition_variable
- acquireLocked() —— 同时锁多个数据
- 使用一把锁,锁多个数据
- struct
- std::tuple
- Benchmark
folly/Synchronized.h 提供了一种更简单、更不容易出错的同步机制,可以用来替代传统 C++标准库中使用较复杂、较容易出错的同步机制。
传统同步方案的缺点
一般是将需要同步的数据和锁一一配对,即 —— associate mutexes with data, not code :
class RequestHandler { ... std::mutex requestMutex_; RequestQueue requestQueue_; processRequest(const Request& request); }; void RequestHandler::processRequest(const Request& request) { std::lock_guard<std::mutex> lg(requestMutex_); requestQueue_.push_back(request); }
然而,操作这些数据成员,开发人员必须注意,正确的获取锁、获取正确的锁。
一些常见的错误包括:
- 操作数据之前没有获取锁。
- 获取了不配对的锁,这个锁不是用来锁这个数据的。
- 获取了读锁,但是试图去修改数据。
- 获取了写锁,但是对数据只有 const access.
一般在使用时,需要提醒开发人员:“别忘了 xxxx”,那一般都会出错,比如 new 的对象别忘了 delete : )
folly/Synchronized.h 简单使用
上面的代码可以用 folly/Synchronized.h 重写为:
class RequestHandler { folly::Synchronized<RequestQueue> requestQueue_; processRequest(const Request& request); }; void RequestHandler::processRequest(const Request& request) { requestQueue_.wlock()->push_back(request); }
为什么 folly/Synchronized.h 更加有效呢?
- 与传统使用方式不同,这里锁和数据是结合成了一个对象 —— requestQueue_。传统方案中,需要寻找锁和数据的配对关系。
- 几乎不可能在不获取锁的情况下,去操作数据,还是因为它们被封装成了一个对象。传统方案加不加锁全靠自觉。
- 在 push_back 后,锁立即被释放。
如果在临界区有多个操作,那么可以使用如下方法:
{ auto lockedQueue = requestQueue_.wlock(); lockedQueue->push_back(request1); lockedQueue->push_back(request2); }
wlock 返回一个 LockedPtr 对象,这个对象可以被理解为指向数据成员的指针。只有这个对象存在,那么锁就会被锁住,所以最好为这个对象显示定义一个 scope.
更好的方式,是使用 lambdas :
void RequestHandler::processRequest(const Request& request) { requestQueue_.withWLock([&](auto& queue "&") { // withWLock() automatically holds the lock for the // duration of this lambda function queue.push_back(request); }); }
使用 withWLock 配合 lambdas 强制定义了一个 scope,更清晰。
Synchronized的模板参数
Synchronized 有两个模板参数,数据类型和锁类型:
template <class T, class Mutex = SharedMutex>
如果不指定第二个模板参数,默认是 folly::SharedMutex。只要被 folly::LockTraits 支持的都可以使用,比如 std::mutex、std::recursive_mutex、std::timed_mutex,。std::recursive_timed_mutex、folly::SharedMutex、folly::RWSpinLock、folly::SpinLock.
根据锁类型的不同,Synchronized 会提供不同的 API:
- 共享锁和升级锁:如果存在 lock_shared()成员函数,Synchronized 会提供 wlock(),rlock(),ulock()三个方法来获取不同的锁类型。其中,rlock()只提供对数据成员 const access.
- 排他锁:lock()
withLock()/withRLock()/withWLock() —— 更易用的加锁方式
withLock()在上面提到过了,可以用来替代 lock()。在持有锁的期间,执行一个 lambda 或者 function. withRLock()/withWLock()同理可以替代 rlock()/wlock().
我们再详细说一下这种方式的好处。下面的函数将 vector 里的所有元素都 double:
auto locked = vec.lock(); for (int& n : *locked) { n *= 2; }
使用 lock()/wlock()/rlock()的一个重要注意事项:一个指向数据的指针或者引用,它的生命周期一定不要比 LockedPtr 对象长(lock()/wlock()/rlock()的返回值类型)。 如果我们将上面的例子这样写就会出问题:
// No. NO. NO! for (int& n : *vec.wlock()) { n *= 2; }
vec.wlock()返回的 LockPtr 对象在 range iterators 建立后就销毁了(详细解释见 Range-based for loop Temporary range expression 小节),range iterators 指向了 vector data,但此时锁已经被释放。想想如果要 debug 这种问题,会用多少时间
这篇关于C++ folly库解读(三)Synchronized —— 比std::lock_guard/std::unique_lock更易用、功能更强大的同步机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享