1是:1个线程安全的队列
2是:两种关系的线程
3是:3种关系
思路:
队列:可以借助STL当中的queue
线程安全:STL中的容器都是线程不安全的
两种角色的线程:
使用C++ queue模拟阻塞队列的生产消费模型代码:
#include
#include
#include
#include
//生产者消费者模型
//1. 一个线程安全的队列
//2. 两个角色的线程 生产线程&消费线程
//3.3种关系
//线程安全的队列
class BlockQueue
{
public:
BlockQueue(int capacity)
{
capacity_ = capacity;
pthread_mutex_init(&lock_,NULL);
pthread_cond_init(&prod_cond_,NULL);
pthread_cond_init(&cons_cond_,NULL);
}
~BlockQueue()
{
//销毁
pthread_mutex_destroy(&lock_);
pthread_cond_destroy(&prod_cond_);
pthread_cond_destroy(&cons_cond_);
}
//push存放数据
void Push(int data)
{
//加锁
pthread_mutex_lock(&lock_);
//如果队列已满,则阻塞等待
while(que_.size()>=capacity_)
{
pthread_cond_wait(&prod_cond_,&lock_);
}
//插入数据
que_.push(data);
//通知消费线程
pthread_cond_signal(&cons_cond_);
//解锁
pthread_mutex_unlock(&lock_);
}
//取出数据
void Pop(int *data)
{
pthread_mutex_lock(&lock_);
//如果队列为空,则阻塞等待
while(que_.empty())
{
pthread_cond_wait(&cons_cond_,&lock_);
}
//出队
*data = que_.front();
que_.pop();
//通知生产线程
pthread_cond_signal(&prod_cond_);
//解锁
pthread_mutex_unlock(&lock_);
}
private:
std::queue<int> que_;
//容量
int capacity_;
//互斥锁
pthread_mutex_t lock_;
//条件变量
pthread_cond_t prod_cond_;
pthread_cond_t cons_cond_;
};
//定义线程数量
#define THREADCOUNT 1
//生产线程入口函数
void* prodStart(void* arg)
{
BlockQueue* bq = (BlockQueue*)arg;
int data = 0;
while(1)
{
bq->Push(data);
printf("i am thread %p,push data:%d\n",pthread_self(),data);
data++;
}
return NULL;
}
//消费线程入口函数
void* consStart(void* arg)
{
BlockQueue* bq = (BlockQueue*)arg;
int data = 0;
while(1)
{
bq->Pop(&data);
printf("i am thread %p,pop data:%d\n",pthread_self(),data);
}
return NULL;
}
int main()
{
BlockQueue *blockQueue = new BlockQueue(10);
//创建线程
pthread_t prod_tid[THREADCOUNT],cons_tid[THREADCOUNT];
for(int i = 0;i<THREADCOUNT;i++)
{
int ret = pthread_create(&prod_tid[i],NULL,prodStart,(void*)blockQueue);
if(ret < 0)
{
perror("pthread_create");
return -1;
}
ret = pthread_create(&cons_tid[i],NULL,consStart,(void*)blockQueue);
if(ret < 0)
{
perror("pthread_create");
return -1;
}
}
//main线程阻塞等待
for(int i = 0;i<THREADCOUNT;i++)
{
pthread_join(prod_tid[i],NULL);
pthread_join(cons_tid[i],NULL);
}
return 0;
}
//编译
g++ $^ -o $@ -g -l pthread
本质:
计数器+PCB等待队列+一堆接口(等待接口+唤醒接口)
计数器:本质是对资源的计数
sem_t:
-eg:sem_t sem;
函数:
函数说明:
sem:传入信号量的地址
pshared:表示当前信号量是使用在进程间还是线程间
当使用sem_init初始化信号量为进程间的时候,会在内核中创建一块共享内存,来保存信号量的数据结构,其中资源计数器,PCB等待队列都是在共享内存当中维护的,所以我们调用唤醒或者等待接口的时候,就通过操作共享内存实现了不同进程间的通信,进而实现不同进程之间的同步与互斥。
value:实际资源数量,用于初始化信号量当中资源计数器的
函数:
作用:
互斥:
同步:
不要求信号量当中的计数器一定为1,也可以为其他正整数
当执行流想要访问临界资源的时候,首先获取信号量
当释放信号量的时候,会对信号量当中的计数器进行加1操作,是否唤醒PCB等待队列当中的执行流
基于环形队列的生产消费模型:环形队列采用数组模拟
代码:
#include
#include
#include
#include
#include
#include
/*
* posix信号量 基于环形队列的生产消费模型
* 本质 计数器+PCB等待队列+一堆接口 计数器:本质是对资源的计数
* 1. 对于数组下标,pos = (pos+1)%数组大小
* 2. 对读写数组实现线程安全的时候
* 互斥: sem_t lock; sem_init(&lock,0,1);
* 同步:
* 生产者信号量: sem_t prod; sem_init(&prod,0,数组大小);
* 由于一开始数组当中没有空间可以读,则计数器初始值为0
* 消费者信号量: sem_t cons; sem_init(&cons,0,0);
*/
//定义数组容量大小
#define CAPACITY 4
//线程安全的队列
class RingQueue
{
public:
RingQueue()
:vec_(CAPACITY)
{
capacity_ = CAPACITY;
//初始化信号量
sem_init(&lock_,0,1);//初始化资源数为1,即只有一个线程在同一时刻能够拥有该信号量
sem_init(&prod_,0,capacity_);// 信号量计数器的值和数组的空闲空间一样大
// 在初始化时,由于数组没有一个有效元素,所有初始化资源数为0,后边生产线程在唤醒消费线程的时候,会对消费者信号量当中的计数器进行加加操作,从而消费者线程可以获取到消费信号量,进而去访问数组
sem_init(&cons_,0,0);
//初始化下标位置
pos_read_ = 0;
pos_write_ = 0;
}
~RingQueue()
{
//销毁
sem_destroy(&lock_);
sem_destroy(&prod_);
sem_destroy(&cons_);
}
void Push(int data)
{
sem_wait(&prod_);
sem_wait(&lock_);
vec_[pos_write_] = data;
pos_write_ = (pos_write_+1)%capacity_;
sem_post(&lock_);
sem_post(&cons_);
}
void Pop(int* data)
{
sem_wait(&cons_);
sem_wait(&lock_);
*data = vec_[pos_read_];
pos_read_ = (pos_read_+1)%capacity_;
sem_post(&lock_);
sem_post(&prod_);
}
private:
//数组
std::vector<int> vec_;
//数组容量
int capacity_;
//信号量
sem_t lock_; // 锁信号量 互斥
//同步
sem_t prod_; // 生产者信号量
sem_t cons_; // 消费者信号量
//读写位置 下标
int pos_write_;
int pos_read_;
};
#define THREADCOUNT 2
//生产线程入口函数
void* prodStart(void* arg)
{
RingQueue* rq = (RingQueue*)arg;
int data = 0;
while(1)
{
rq->Push(data);
printf("i am prod_thread %p,push data:%d\n",pthread_self(),data);
data++;
}
return NULL;
}
//消费线程入口函数
void* consStart(void* arg)
{
RingQueue* rq = (RingQueue*)arg;
int data;
while(1)
{
rq->Pop(&data);
printf("i am cons_thread %p,pop data:%d\n",pthread_self(),data);
}
return NULL;
}
int main()
{
RingQueue* rq = new RingQueue();
//创建线程
pthread_t prod_tid[THREADCOUNT],cons_tid[THREADCOUNT];
for(int i=0;i<THREADCOUNT;i++)
{
int ret = pthread_create(&prod_tid[i],NULL,prodStart,(void*)rq);
if(ret<0)
{
perror("pthread_create");
return -1;
}
ret = pthread_create(&cons_tid[i],NULL,consStart,(void*)rq);
if(ret<0)
{
perror("pthread_create");
return -1;
}
}
for(int i = 0;i<THREADCOUNT;i++)
{
pthread_join(prod_tid[i],NULL);
pthread_join(cons_tid[i],NULL);
}
delete rq;
rq = NULL;
return 0;
}
//编译
g++ $^ -o $@ -g -l pthread
在编写多线程时,有一种情况十分常见,那就是大量读,少量写的场景。
机制:
函数:
函数说明:
函数:
函数:
函数说明:
函数:
函数:
函数说明:
函数:
函数说明:
attr:读写锁属性
pref:属性宏定义
#include
#include
#include
#include
#include
#include
#include
#include
//读写锁
volatile int ticket = 1000;
//定义读写锁
pthread_rwlock_t rwlock;
//读
void* reader(void* arg)
{
char* id = (char*)arg;
while(1)
{
//加锁
pthread_rwlock_rdlock(&rwlock);
if(ticket<=0)
{
//解锁
pthread_rwlock_unlock(&rwlock);
break;
}
printf("%s: %d\n",id,ticket);
//解锁
pthread_rwlock_unlock(&rwlock);
usleep(1);
}
return NULL;
}
//写
void* writer(void* arg)
{
char* id = (char*)arg;
while(1)
{
pthread_rwlock_wrlock(&rwlock);
if(ticket<=0)
{
pthread_rwlock_unlock(&rwlock);
break;
}
printf("%s: %d\n",id,--ticket);
pthread_rwlock_unlock(&rwlock);
usleep(1);
}
return NULL;
}
struct ThreadAttr
{
pthread_t tid;
std::string id;
};
std::string create_reader_id(size_t i)
{
//利用ostringstream进行string 拼接
std::ostringstream oss("thread reader ",std::ios_base::ate);
oss<<i;
return oss.str();
}
std::string create_writer_id(size_t i)
{
std::ostringstream oss("thread writer ",std::ios_base::ate);
oss << i;
return oss.str();
}
//创建读线程
void init_readers(std::vector<ThreadAttr>& vec)
{
for(int i = 0;i<vec.size();i++)
{
vec[i].id = create_reader_id(i);
int ret = pthread_create(&vec[i].tid,NULL,reader,(void*)vec[i].id.c_str());
if(ret < 0)
{
perror("pthread_create");
return;
}
}
}
// 创建写线程
void init_writers(std::vector<ThreadAttr>& vec)
{
for(int i = 0;i<vec.size();i++)
{
vec[i].id = create_writer_id(i);
int ret = pthread_create(&vec[i].tid,NULL,writer,(void*)vec[i].id.c_str());
if(ret < 0)
{
perror("pthread_create");
return;
}
}
}
//回收线程
void join_threads(std::vector<ThreadAttr>const& vec)
{
//vector的迭代器
for(std::vector<ThreadAttr>::const_reverse_iterator it = vec.rbegin();it != vec.rend();it++)
{
pthread_t const& tid = it->tid;
pthread_join(tid,NULL);
}
}
//初始化读写锁
void init_rwlock()
{
#if 0
//设置写优先
pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NP);
pthread_rwlock_init(&rwlock,&attr);
pthread_rwlockattr_destroy(&attr);
#else
//默认读优先,会造成写饥饿
pthread_rwlock_init(&rwlock,NULL);
#endif
}
int main()
{
//定义写线程数量
const std::size_t reader_nr = 2000;
//定义读线程数量
const std::size_t writer_nr = 2;
std::vector<ThreadAttr> readers(reader_nr);
std::vector<ThreadAttr> writers(writer_nr);
//初始化读写锁
init_rwlock();
//创建读线程
init_readers(readers);
//创建写线程
init_writers(writers);
//main阻塞等待回收线程
join_threads(writers);
join_threads(readers);
//销毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
//编译
g++ $^ -o $@ -g -l pthread
线程池:
应用场景:
线程池中的线程,都是同一种角色的线程,言外之意,指的是每一个线程都执行同样的入口函数。
那么如何让相同入口函数的线程,处理不同的请求:
线程处理数据采用的方式:
考虑让线程池中的线程退出:
#include
#include
#include
#include
#include
#include
//重定义一个返回值为void,参数为int的函数指针类型
typedef void (*Handler)(int);
//数据和处理数据的方式类
class QueueData
{
public:
QueueData(int data,Handler handler)
{
data_ = data;
handler_ = handler;
}
~QueueData(){}
//处理数据
void Run()
{
handler_(data_);
}
private:
int data_;
Handler handler_;
};
//线程池
class ThreadPool
{
public:
ThreadPool(int capacity,int thread_count)
{
capacity_ = capacity;
thread_count_ = thread_count;
//初始化互斥锁
pthread_mutex_init(&lock_,NULL);
//初始化条件变量
pthread_cond_init(&cons_cond_,NULL);
flag = 0;
}
~ThreadPool() //销毁
{
pthread_mutex_destroy(&lock_);
pthread_cond_destroy(&cons_cond_);
}
//创建线程
int THread_init()
{
pthread_t tid;
for(int i=0;i<thread_count_;i++)
{
int ret = pthread_create(&tid,NULL,PoolStart,(void*)this);
if(ret < 0)
{
perror("pthrad_create");
return -1;
}
}
return 0;
}
//往队列中push数据
int Push(QueueData* qd)
{
//加锁
pthread_mutex_lock(&lock_);
if(flag)
{
//退出前解锁
pthread_mutex_unlock(&lock_);
return -1;
}
que_.push(qd);
//解锁
pthread_mutex_unlock(&lock_);
//通知消费者线程
pthread_cond_signal(&cons_cond_);
return 0;
}
private:
void Pop(QueueData** qd)
{
*qd = que_.front();
que_.pop();
}
//线程入口函数
static void* PoolStart(void* arg)
{
//线程分离
pthread_detach(pthread_self());
ThreadPool *tp = (ThreadPool*)arg;
while(1)
{
//加锁
pthread_mutex_lock(&tp->lock_);
while(tp->que_.empty())
{
//等待
// 判断是否退出线程
if(tp->flag)
{
tp->thread_count_--;
pthread_mutex_unlock(&tp->lock_);
pthread_exit(NULL);
}
//队列为空进入等待
pthread_cond_wait(&tp->cons_cond_,&tp->lock_);
}
//队列中有数据
QueueData* qd;
tp->Pop(&qd);
//解锁
pthread_mutex_unlock(&tp->lock_);
//处理数据
qd->Run();
delete qd;
}
}
public:
//线程退出
void ThreadExit()
{
//加锁
pthread_mutex_lock(&lock_);
flag = 1;
pthread_mutex_unlock(&lock_);
//通知全部处于等待的线程
pthread_cond_broadcast(&cons_cond_);
}
private:
//线程安全的队列
size_t capacity_;//队列大小
std::queue<QueueData*> que_;
//互斥锁
pthread_mutex_t lock_;
//条件变量
pthread_cond_t cons_cond_; // 都是消费线程,只需要消费线程的条件变量
//线程的数量个数
int thread_count_;
// 中断标志位 线程退出标志
int flag;
};
//处理数据的函数
void DealData(int data)
{
printf("data:%d\n",data);
}
int main()
{
//创建一个线程池,队列大小为4,线程数量为2
ThreadPool* tp = new ThreadPool(4,2);
if(!tp)
{
printf("create ThreadPool failed\n");
return -1;
}
//创建线程
int ret = tp->THread_init();
if(ret<0)
{
printf("create ThreadPool failed\n");
return -1;
}
for(int i = 0;i<100;i++)
{
//向队列中存放数据
QueueData* qd = new QueueData(i,DealData);
if(!qd)
{
continue;
}
tp->Push(qd);
}
sleep(10);//休眠10s后让线程池中的线程退出
tp->ThreadExit();
delete tp;
#if 0
while(1)
{
sleep(1);
}
#endif
return 0;
}
设计模式的优点:
设计模式的分类:
特点:
使用场景:
基础要点:
程序启动的时候就进行初始化,资源在程序初始化的时候就全部加载完毕了
代码示例:
template<class T>
class Singleton
{
private:
static T instance;//禁止构造,拷贝构造,赋值拷贝
public:
static T* GetInstance()
{
return &instance;
}
};
资源在使用的时候才进行实例化,单例类的对象在使用的时候才进行实例化
代码示例:
template<class T>
class Singleton
{
private:
static T* instance;
public:
static T* GetInstance()
{
if(instance==NULL)
{
instance = new T();
}
return instance;
}
};
代码示例:
template<class T>
class Singleton
{
private:
static T* instance;
static std::mutex lock;
public:
static T* GetInstance()
{
//加锁
lock.lock();
if(instance == NULL)
{
instance = new T();
}
//解锁
lock.unlock();
return instance;
}
};
代码示例:
template<class T>
class Singleton
{
private:
volatile static T* instance; // volatile 禁止指令重排序
static std::mutex lock;
public:
static T* GetInstance()
{
if(instance==NULL)
{
//加锁
lock.lock();
if(instance==NULL) // 双重校验锁 ,降低锁冲突的概率,提高性能
//使用互斥锁,保证多线程情况下也只调用一次new
{
instance = new T();
}
//解锁
lock.unlock();
}
return instance;
}
};