多线程一直是编程中的重要的工具,它可以分充分的利用硬件资源,是我们用更少的时间去完成更多的事情。在之前的博客中,我有介绍了OpenMP的基本使用,OpenMP可以理解为多线程的一个合理和高效的一套抽象工具。这次,打算仔细的介绍多线程编程中的常见的概念和典型的案例。
典型的案例
说到多线程,最核心的问题就是保证数据的读写安全。为了达到此目的,我们需要多很多常见的数据结构做一些改造,从而适应多线程的场景。以下是我工作中比较常见到的一些使用场景:
- 线程池
- 读写锁
- 消息队列
- ConcurrentCache
- PingPang Buffer
在具体介绍这些使用场景之前,我们还是需要了解需要使用到的一些基本的工具:互斥量、条件变量、原子操作等。
互斥量
互斥量,顾名思义,就是互斥的数据,一个线程持有的时候,其他线程就必须等待。
在C++11中,使用<mutex>
头文件引入。以下是一个简单的计数器的实现。
emit
函数通过mutex_
进行加锁,使得同一时间仅有一个线程可以执行++ x_
的操作,从而保证了计数的正确性。
std::lock_guard
是个工具类,lck在构造时,调用了lock函数,在析构时调用了unlock,从而避免我们自行调用的时候忘记unlock。
#include <mutex>
#include <thread>
#include <iostream>
class Counter {
public:
Counter(): x_(0) {}
void emit() {
mutex_.lock();
++ x_;
mutex_.unlock();
// or
// std::lock_guard<std::mutex> lck(mutex_);
// ++ x_;
}
int count() {
return x_;
}
private:
int x_;
std::mutex mutex_;
};
int main() {
Counter c;
std::thread t1([&c]{
for (int i = 0; i < 10000000; ++ i) {
c.emit();
}
});
std::thread t2([&c]{
for (int i