互斥锁是用来保护一段临界区的,它可以保证某段时间内只有一个线程在执行一段代码或者访问某个资源。
C++11的mutex和lock_guard
C++11新增了mutex,使用方法和linux底下的常用使用方法差不多。先使用std::mutex 定义一个互斥锁,例如std::mutex XXXmutex在需要使用互斥锁的时候,使用XXXmutex.lock();上锁,以及使用XXXmutex.unlock();
解锁。下面是一段参考代码:
#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>
std::map<std::string, std::string> g_pages;
std::mutex g_pages_mutex;
void save_page(const std::string &url)
{
// simulate a long page fetch
std::this_thread::sleep_for(std::chrono::seconds(2));
std::string result = "fake content";
g_pages_mutex.lock();
g_pages[url] = result;
g_pages_mutex.unlock();
}
int main()
{
std::thread t1(save_page, "http://foo");
std::thread t2(save_page, "http://bar");
t1.join();
t2.join();
g_pages_mutex.lock(); // not necessary as the threads are joined, but good style
for (const auto &pair : g_pages) {
std::cout << pair.first << " => " << pair.second << '
';
}
g_pages_mutex.unlock();
}
t2.join();
}
使用mutex的时候需要手动上锁还有解锁,C++11提供了另一个利器–>lock_guard,它是与mutex配合使用,把锁放到lock_guard中时,mutex自动上锁,lock_guard析构时,同时把mutex解锁。使用的应该就是所谓的RAII技法(RAII,,也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。)
下面是另一段示例代码:
#include <thread>
#include <mutex>
int g_i = 0;
std::mutex g_i_mutex; // protects g_i
void safe_increment()
{
std::lock_guard<std::mutex> lock(g_i_mutex);
++g_i;
// g_i_mutex is automatically released when lock
// goes out of scope
}
int main()
{
std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join();
t2.join();
}
使用lock_guard的时候不需要手动解锁,资源获取即初始化,析构的时候就会自动解锁,使用起来会相对比较方便。
muduo的MutexLock 与MutexLockGuard
muduo库里面也封装了两个类似的类:MutexLock 与MutexLockGuard。
MutexLock 封装临界区(critical section),这是一个简单的资源类,用RAII 手法[CCS,条款13] 封装互斥器的创建与销毁。临界区在Windows 上是struct CRITICAL_SECTION,是可重入的;在Linux 下是pthread_mutex_t,默认是不可重入的。MutexLock 一般是别的class 的数据成员。
MutexLockGuard 封装临界区的进入和退出,即加锁和解锁。MutexLockGuard 一般是个栈上对象,它的作用域刚好等于临界区域。
MutexLockGuard类使用RAII技法封装,在实际应用中这个类更常用,作为class 数据成员的MutexLock 只能用于同步本class 的其他数据成员的读和写,它不能保护安全地析构。因为MutexLock 成员的生命期最多与对象一样长,而析构动作可说是发生在对象身故之后(或者身亡之时)。
使用方法:muduo的MutexLock 与MutexLockGuard使用方法也是很简单的,MutexLockGuard负责加锁解锁,MutexLock 封装临界区,所以MutexLockGuard需要和MutexLock 配合使用,一般应先使用MutexLock定义一个临界区,然后将该MutexLock作为参数传进MutexLockGuard对象,例如:
MutexLock mutex_;//互斥锁
MutexLockGuard lock(mutex_);
这样在MutexLockGuard对象生命期结束的时候会自动解锁。那么他的生命期什么时候结束呢?一般如果只是上锁一小段代码,而该代码后面还有很多的语句,那么可以使用大括号将想要锁定的区域括起来。