学习内容:
学习c++中的锁
在C++中,锁是一种同步机制,用于保护共享资源,以防止多个线程同时访问或修改该资源,从而避免数据竞争和不一致性。C++标准库提供了多种锁的实现,包括互斥锁、读写锁和条件变量。
死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
在使用锁的时候,需要注意以下几点:
总之,锁是一种重要的并发编程工具,在多线程程序中起到了保护共享资源的作用。正确使用锁能够有效地避免数据竞争和不一致性的问题,提高程序的并发性能和稳定性。
C++中的互斥锁(mutex)是用于保护共享资源的重要工具。下面是互斥锁常见的使用方式:
std::mutex
类创建互斥锁对象。std::mutex mtx;
std::mutex
类的lock
成员函数可以锁定互斥锁。如果互斥锁已被其他线程锁定,则当前线程会被阻塞直到互斥锁可用。mtx.lock();
std::mutex
类的try_lock
成员函数可以尝试锁定互斥锁。如果互斥锁已被其他线程锁定,则try_lock
函数会立即返回false
,不会阻塞当前线程。if (mtx.try_lock()) {
// 互斥锁已成功被当前线程锁定
} else {
// 互斥锁已被其他线程锁定
}
/*
trylock():查看是否上锁,它有下列3种类情况:
(1)未上锁返回false,并锁住;
(2)其他线程已经上锁,返回true;
(3)同一个线程已经对它上锁,将会产生死锁。
*/
std::mutex
类的unlock
成员函数可以解锁互斥锁。注意,在解锁之前,当前线程必须已锁定互斥锁。mtx.unlock();
std::unique_lock
类可以实现自动加锁和解锁。std::unique_lock
对象在创建时自动加锁,析构时自动解锁。std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx); // 加锁
// 锁定期间对共享资源的操作
// 解锁
lock_guard是C++11引入的一种用于简化线程锁的RAII(资源获取即初始化)封装类。它是一个模板类,用于管理一个std::mutex或std::recursive_mutex的锁。
通过使用lock_guard,可以在一个代码块内自动获取锁,并在代码块结束时自动释放锁,从而确保线程安全。
lock_guard类的定义如下:
template <class Mutex>
class lock_guard {
public:
explicit lock_guard(Mutex& mtx);
lock_guard(const lock_guard&) = delete;
lock_guard& operator=(const lock_guard&) = delete;
~lock_guard();
};
构造函数lock_guard接受一个互斥量(Mutex)的引用作为参数,并在构造时自动获取锁。析构函数在对象销毁时自动释放锁。
使用lock_guard的一般步骤为:
recursive_mutex
的实例。例如,下面的代码片段展示了如何使用lock_guard来保证一个共享整数的线程安全访问:
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mtx;
int sharedData = 0;
void increment() {
std::lock_guard<std::mutex> lock(mtx);
sharedData++;
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final value of sharedData: " << sharedData << std::endl;
return 0;
}
在上述代码中,两个线程t1和t2同时执行increment函数来增加sharedData的值。由于lock_guard的存在,每个线程在执行increment函数时都会获取互斥量mtx的锁,从而确保了对sharedData的线程安全访问。
需要注意的是
,由于lock_guard的析构函数会自动释放锁,因此不建议手动调用unlock函数来释放锁。如果需要手动释放锁,可以使用std::unique_lock来替代lock_guard。
unique_lock是C++标准库提供的一种互斥锁管理器。它允许对互斥量进行自动加锁和解锁操作,从而减少了手动管理锁的复杂性和错误。
unique_lock提供了更灵活的锁管理功能,与lock_guard相比,它具有更多的特性和控制权。unique_lock可以在创建时选择是否锁定互斥量,也可以在任何时候手动锁定或解锁互斥量。
unique_lock的使用方式类似于std::lock_guard,可以通过构造函数或成员函数来锁定互斥量。它还提供了额外的功能,如延迟锁定、尝试锁定和条件变量支持。
unique_lock还支持移动语义,即可以将已经锁定的unique_lock对象从一个线程传递给另一个线程。这样可以方便地在函数间传递锁的所有权,避免了手动解锁和重新锁定的麻烦。
unique_lock还可以与条件变量一起使用,用于实现复杂的线程同步操作。当条件不满足时,可以释放锁并等待条件满足,一旦条件满足,就可以重新获取锁并继续执行。
总之,unique_lock提供了更灵活、更安全的互斥锁管理功能,可以帮助开发者编写出更可靠、高效的多线程程序。它是C++多线程编程中的重要工具之一。
unique_lock提供了以下几个重要的成员函数:
lock():锁定互斥量。
unlock():解锁互斥量。
try_lock():尝试锁定互斥量,如果互斥量已经被其他线程锁定,则返回false。
release():将unique_lock对象和互斥量的关联解除,返回持有的互斥量,并且unique_lock对象不再管理该互斥量。
reset():将unique_lock对象重新与互斥量关联起来。
unique_lock还提供了以下两个成员函数用于等待条件变量:
wait():等待条件变量满足,并释放互斥量。当满足条件时,wait()重新获取互斥量并返回。
wait_for():等待一段时间内条件变量满足,并释放互斥量。当满足条件或超时时,wait_for()重新获取互斥量并返回。
例如:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int count = 0;
void increment()
{
// 创建unique_lock对象,并锁定互斥量
std::unique_lock<std::mutex> lock(mtx);
for (int i = 0; i < 100000; ++i)
{
count++;
}
// lock析构时自动解锁互斥量
}
int main()
{
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "count: " << count << std::endl;
return 0;
}
在上面的例子中,创建了两个线程t1和t2,它们都会调用increment函数来递增变量count的值。increment函数中创建了一个unique_lock对象,并锁定了互斥量mtx。这样就能保证在任意时刻只能有一个线程访问count变量,避免了数据竞争的问题。
在主函数中,我们等待两个线程的完成,并输出count的值。由于互斥量的保护,我们可以确保count的值最终是正确的。