随着单例模式的完成,linux的系统部分(还有网络哦)逐渐告一段落,是时候用一个小项目来总结一下线程向的学习了
首先是挑选单例模式:饿汉模式与懒汉模式,这里我选择不太麻烦的饿汉模式,也就是正常写,少用new来简化代码,要啥写啥,那么饿汉模式的问题是啥呢?代码冗余对服务器的速度要求高,不过我这几百行的代码也冗余不到哪里去,所以我就选择做一个饥饿的男人
然后是线程池,线程池就是把一个进程进一步细分成不同的任务,当然你都把系统调用的基本单位进程又往下分了,人家系统自己安排一下线程的顺序不过分吧?这一安排就引发了很多问题,你的进程就像一个停车场(进程),有很多车(线程)要往上停,一段时间后,肯定会发生这样的情况:有时候全是往出开的车,反而没有车往进开,大家挤来挤去出去的车更少了,而进来的入口被浪费了(线程饥饿),车太多了收费员忙不过来把隔壁车的车费结算到你头上了(线程混乱),你打算结清车费把付款吗亮出来结果被别人盗刷了(线程安全),所以,收费员用栅栏给停车场规划了一下(锁),修筑了了一车一杆的收费亭(临界区),使每次进出的只能有一辆车(互斥锁),还在大屏幕上显示了空余车位的数量,如果不够那么进来停车的人需要等待一会(线程等待),这样,我们的线程池运行就能处于一个比较丝滑的状态
还有就是我们要实现的cp模型,就是模拟消费者和生产者的关系:消费者与消费者的关系是互斥的(两个人不能同时扫码),生产者与生产者的关系是互斥的(一家厂房不能有两个企业),消费者和生产者在建立上是互斥的,在行为上是同步的(可以同时卖和买,但是消费者不能去人家生产者的厂子里吃),这就是我们用锁的规划
下面来正式实现项目
首先是Makefile
注意因为nullptr的存在必须用c++11,而且要使用外部库pthread(锁嘛)
然后是head1.hpp
#include<iostream>
#include<pthread.h>
#include<queue>
template <class T>
class blockqueue
{
private:
std::queue<T>q;
int maxnum;
pthread_mutex_t mutex;
pthread_cond_t c;
pthread_cond_t p;
public:
blockqueue(int a=20):maxnum(a)
{
pthread_mutex_init(&mutex,nullptr);
pthread_cond_init(&c,nullptr);
pthread_cond_init(&p,nullptr);
}
~blockqueue()
{
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&c);
pthread_cond_destroy(&p);
}
void push( T &in)
{
pthread_mutex_lock(&mutex);
while(q.size()==maxnum)
{
pthread_cond_wait(&p,&mutex);
}
q.push(in);
pthread_cond_signal(&c);
pthread_mutex_unlock(&mutex);
}
T pop()
{
pthread_mutex_lock(&mutex);
while(q.size()==0)
{
pthread_cond_wait(&c,&mutex);
}
T out=q.front();
q.pop();
pthread_cond_signal(&p);
pthread_mutex_unlock(&mutex);
return out;
}
};
这是我们的等待队列(有多线程和cp模型就必有它)(先入先出模拟清仓处理,这是我的一点拙见,如果要先入后出就写一个等待栈呗)
首先是队列长度和两个条件变量c与p,指生产者和消费者,构造函数和析构函数不必多说,来聊一下push和pop的逻辑,首先是push,参数是一个模板对象,首先队列有空位才能用吧,如果满了就让p等待一下,别生产了,有空就插入队列并且给c发个消息可以来消费了,pop函数也大同小异,值得注意的是要使用互斥锁
接下来是head2.hpp中的Task类,简单写了一个
#pragma once
#include <iostream>
#include <string>
class Task
{
public:
Task()
{
std::cout<<"生产了一个对象,静待消费者"<<std::endl;
}
void work()
{
std::cout<<"消费了一个对象,生产者快来补货"<<std::endl;
}
};
最后是text.cc
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include <queue>
#include <mutex>
#include <condition_variable>
#include"head1.hpp"
#include"head2.hpp"
void *cwork(void* args)
{
blockqueue<Task>*bq=static_cast<blockqueue<Task>*>(args);
Task t=bq->pop();
t.work();
}
void *pwork(void* args)
{
blockqueue<Task>*bq=static_cast<blockqueue<Task>*>(args);
Task t;
bq->push(t);
}
int main()
{
srand(time(nullptr));
blockqueue<Task>*bq=new blockqueue<Task>();
pthread_t c[4],p[5];
for(int i=0;i<4;i++)
{
pthread_create(c+i,nullptr,cwork,bq);
}
for(int j=0;j<5;j++)
{
pthread_create(p+j,nullptr,pwork,bq);
}
for(int i=0;i<4;i++)
{
pthread_join(c[i],nullptr);
}
for(int j=0;j<5;j++)
{
pthread_join(p[j],nullptr);
}
delete bq;
}
cwork消费者函数就是调用类函数,pwork就是创建类,我特地多搞了几个类,要不然会有可怜的消费者在一直等
感兴趣的uu可以来搞一下这个小项目
优点:
1.对cp模型有一个更全面的理解
2.对多线程操作有一个整体的认识
3.会用锁
4.变量类型唬人,代码复杂,多文件操作,外行看热闹内行看热闹,可以小装一波
5.快的几个小时搞定