本文主要讲述C++11线程同步原语,以及使用场景。
C++11 线程同步原语有三个:mutex(互斥锁),condition variable(条件变量)和semaphore(信号量)。
一 mutex
mutex是最简单的同步原语,用来防止多个线程并发的访问临界区资源。如果应用中有些资源在同一时间最多只能被一个线程访问,那么就使用mutex来保护这些资源。
使用方法也非常简单,总共3步,
- 加锁
- 执行临界区代码
- 释放锁
下面是简单的范例代码,一般配合std::lock_guard或者std::unique_lock来使用
#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)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
std::string result = "fake content";
std::lock_guard<std::mutex> guard(g_pages_mutex);
g_pages[url] = result;
}
int main()
{
std::thread t1(save_page, "http://foo");
std::thread t2(save_page, "http://bar");
t1.join();
t2.join();
for (const auto &pair : g_pages) {
std::cout << pair.first << " => " << pair.second << '\n';
}
return 0;
}
加锁由guard的构造函数来完成,释放锁由guard的析构函数来完成,只要出了guard的作用域就会自动释放。
PS: std::mutex提供的互斥锁是non-recursive的
二 condition variable
条件变量用于实现多个线程间的notification/synchronization机制,使用场景如下,
Condition variable allows a thread T to wait for completion of a given event on a particular object (some shared state, data structure, anything). The event over here is really the change in state of some condition that thread is interested in. Until that is satisfied, the thread waits to be awakened later by a signalling thread (that actually changes the condition).
这个notification/synchronization机制是mutex无法做到的。
条件变量的使用需要3个元素,
- 条件变量 (同步原语)
- 互斥锁
- 条件 (一个共享变量或其它)
下面是个简单范例,代码中cv是条件变量,m是互斥锁,ready和processed是条件
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
std::cout << "Worker thread is processing data\n";
data += " after processing";
processed = true;
std::cout << "Worker thread signals data processing completed\n";
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
return 0;
}
运行逻辑如下,
- worker_thread调用cv.wait()时,条件ready是false,就会释放互斥锁,然后在那里等待通知
- 主线程先去拿锁,然后把条件ready修改为true,接着释放锁,最后通知worker_thread
- worker_thread接到通知后发现条件为真,又会获取锁,接着处理data,并把条件processed置位true
- 同理可以分析条件processed
三 semaphore
信号量提供了以下2个特性:
- 允许N个线程并发访问临界区,N与应用相关
- 提供了类似条件变量的notification/synchronization机制
本人没有使用过信号量…
|