看下面两个函数的例子,分别使用了lock_guard和unique_lock:
std::lock_guard锁:
unsigned int DataFifoList::write(const char *buf, unsigned int size) {
std::lock_guard<std::mutex> lock(m_mtx);
if (size > FreeSize()) {
printf("fifo full!\n");
Clear();
return 0;
}
// 数据写入操作
...
return size;
}
- 锁的类型: 使用的是 std::lock_guardstd::mutex。
- 锁的特点:
- 自动锁定: 在 lock_guard 对象创建时,自动锁定互斥锁 m_mtx。
- 自动解锁: 在函数作用域结束时,lock_guard 对象被销毁,互斥锁自动解锁。
- 不可解锁/重新锁定: 一旦锁定,lock_guard 对象不能手动解锁和重新锁定。因此,锁定操作的粒度固定,从函数进入到退出整个过程中保持锁定。
- 适用场景: std::lock_guard 适用于整个函数或代码块中锁定逻辑简单且不需要手动控制锁的情况。
std:unique_lock:
unsigned int DataFifoList::read(char *buf, unsigned int size, int us) {
{
std::unique_lock<std::mutex> lock(m_mtx);
while (m_used < size) {
if (us >= 0) {
lock.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(1));
lock.lock();
us--;
if (us <= 0) return 0;
} else {
lock.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(1));
lock.lock();
}
}
// 数据读取操作
...
}
return size;
}
-
锁的类型: 使用的是 std::unique_lockstd::mutex。
-
锁的特点:
- 灵活性更高: 与 std::lock_guard 不同,std::unique_lock 允许显式解锁和重新锁定。这在 read 函数中是非常关键的。
- 手动解锁/重新锁定: 在等待数据可用时,lock 被显式解锁,释放互斥锁以避免长时间占用,然后在适当的时候重新锁定。
- 可延迟锁定: 虽然这个函数中没有使用延迟锁定的特性,但 std::unique_lock 可以在构造时选择不立即锁定互斥锁,而是在需要时再锁定。
-
适用场景: std::unique_lock 适用于需要在代码中多次解锁和重新锁定互斥锁的复杂场景,比如需要在等待条件满足时释放锁以避免阻塞其他线程。
总结:
-
write 函数: 使用 std::lock_guardstd::mutex 进行简单的、整个函数范围内的锁定操作。这种方式锁定粒度固定且较小,适用于整个操作都需要在锁定状态下完成的情况。
-
read 函数: 使用 std::unique_lockstd::mutex,允许在等待数据时释放锁,并在数据可用后重新锁定。这种方式提供了更高的灵活性,可以避免在等待过程中长时间占用锁,使得其他线程能够在此期间获得锁。