目录
个人主页:东洛的克莱斯韦克-CSDN博客
引言
STL容器并没有保证线程安全,而大多数应用场景下,为了追求效率,多线程是必不可少的。而底层容器难免会有并发问题。从设计上来说要么在上层代码做加锁处理,要么封装出能保证线程安全容器。
本文给出的方案是用信号量和互斥量封装vector容器------用两个信号量和两个互斥量封装出环形队列。
容器模型
给容器设计两个接口,push()用来向容器填充数据,pop用来向容器取数据。调用push接口的称为生产者,调用pop接口的称为消费者。
vector容器用下标回绕的方式,在逻辑上是一个环形。线程先去申请信号量资源,申请到了信号量资源的线程再去竞争互斥量,谁能锁住互斥量,谁就去容器里操作。
有了信号量和互斥量的存在,在任意时刻,有且只能有0或1个生产者,0或1个消费者线程在容器里操作。那么push接口和pop接口就是原子性的操作。
容器代码
cpp
#pragma once // 防止头文件被重复包含
//保证线程安全的环形队列容器
#include <vector>
#include <semaphore.h>
#include <pthread.h>
#define C_MAX 300 //容器的容量
template <class T>
class annular
{
public:
annular(int max_c = C_MAX)
: max_capacity(max_c), min_capacity(0), _c_subscript(0), _p_subscript(0)
{
_v.reserve(max_c);
pthread_mutex_init(&_c_lock, nullptr);
pthread_mutex_init(&_p_lock, nullptr);
sem_init(&_c_sem, 0, 0);
sem_init(&_p_sem, 0, max_c);
}
~annular()
{
pthread_mutex_destroy(&_c_lock);
pthread_mutex_destroy(&_p_lock);
sem_destroy(&_c_sem);
sem_destroy(&_p_sem);
}
void push(const T &data)
{
sem_wait(&_p_sem); // 信号量的p操作
pthread_mutex_lock(&_p_lock); // 加锁
_v[_p_subscript] = data;
_p_subscript++;
_p_subscript %= max_capacity;
sem_post(&_c_sem); // 信号量的v操作
pthread_mutex_unlock(&_p_lock); // 解锁
}
void pop(T &data)
{
sem_wait(&_c_sem); // 信号量的p操作
pthread_mutex_lock(&_c_lock); // 加锁
data = _v[_c_subscript];
_c_subscript++;
_c_subscript %= max_capacity;
sem_post(&_p_sem); // 信号量的v操作
pthread_mutex_unlock(&_c_lock); // 解锁
}
private:
pthread_mutex_t _c_lock; // 消费者互斥量
pthread_mutex_t _p_lock; // 生产者互斥量
sem_t _c_sem; // 消费者信号量
sem_t _p_sem; // 生产者信号量
int _c_subscript; // 消费者下标
int _p_subscript; // 生产者下标
std::vector<T> _v;
int max_capacity; // 容器最大容量
int min_capacity; // 容器最小容量
};