linux:线程 POSIX信号量&&线程池

POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

1.POSIX信号量的系统调用函数

头文件:#include<semaphore.h>

(1)初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);

参数:
pshared:0表示线程间共享,非零表示进程间共享。
value:信号量初始值。

(2)销毁信号量
int sem_destroy(sem_t *sem);
(3)等待信号量

P操作,等待信号量,会将信号量的值减1,请求资源。

int sem_wait(sem_t *sem); //P操作
(4)发布信号量

V操作,发布信号量,将信号量值加1,释放资源。

int sem_post(sem_t *sem);//V操作

2.基于环形队列的生产消费模型

环形队列采用数组模拟,用模运算来模拟环状特性;当下标++至数组尾部时,将其置为0。

#pragma once
#include<iostream>
#include<semaphore.h>
#include<sys/sem.h>
#include<vector>
#include<unistd.h>
using namespace std;
#define NUM 10//宏定义全局变量NUM,作为环形队列容量大小

class ringQueue
{
private:
  vector<int> v;//vector一个数组模拟环形队列
  int max_cap;//环形队列的容量
  sem_t sem_blank;//队列中空格;生产者所关注
  sem_t sem_data;//队列中数据;消费者所关注
  int con_i;//生产者索引
  int pro_i;//消费者索引
public:
  //P操作,等待信号量,会将信号量的值减1,请求资源
  void P(sem_t &s)
  {
    sem_wait(&s);
  }
  //V操作,发布信号量,将信号量值加1,释放资源
  void V(sem_t &s)
  {
    sem_post(&s);
  }
public:
  ringQueue(int _cap=NUM):max_cap(_cap),v(_cap)
  {
    //刚开始时,blank(空格子)数量为NUM,data(数据)数量为0;两者下标均为0;
    sem_init(&sem_blank,0,NUM);
    sem_init(&sem_data,0,0);
    con_i=0;pro_i=0;
  }
  ~ringQueue()
  {
    sem_destroy(&sem_blank);
    sem_destroy(&sem_data);
    con_i=pro_i=0;
  }
  //消费者消费;取出值
  void Get(int &out)
  {
    //请求data资源并取出
    P(sem_data);
    out=v[con_i];
    con_i++;
    //取完数据后将消费者下标con_i归零
    con_i%=max_cap;
    //释放blank资源
    V(sem_blank);
  }
  //生产者生产:放入值
  void Put(const int &in)
  {
    //请求blank资源并放入数据
    P(sem_blank);
    v[pro_i]=in;
    pro_i++;
    //放满数据后将消费者下标con_i归零
    pro_i%=max_cap;
    //释放data资源
    V(sem_data);
  }
};
(1)生产快&&消费慢
#include "ringQueue.h"
using namespace std;
//在环形队列中取出数据,每取出一个数据之后打印"consumer done!"。
void *consumer(void *ring_queue)
{
  ringQueue *rq=(ringQueue*)ring_queue;
  while(true)
  {
    sleep(1);
    int data=1;
    rq->Get(data);
    cout<<"consumer done# "<<data<<endl;
  }
}
//向环形队列中放入数据,每放入一次数据之后打印"productor done!"。
void *productor(void *ring_queue)
{
  ringQueue *rq=(ringQueue*)ring_queue;
  int count=0;
  while(true)
  {
    //sleep(1);
    rq->Put(count);
    count++;
    if(count==9)
    {
      count=0;
    }
    cout<<"productor done!"<<endl;
  }
}
int main()
{
  pthread_t con,pro;
  ringQueue *rq=new ringQueue();

  pthread_create(&con,nullptr,consumer,rq);
  pthread_create(&pro,nullptr,productor,rq);

  pthread_join(con,nullptr);
  pthread_join(pro,nullptr);

  return 0;
}

运行截图
在这里插入图片描述

(1)生产慢&&消费快

只是让消费者取出数据之后不在sleep(1);而是让生产者放入数据之后sleep(1)。

#include "ringQueue.h"
using namespace std;
//在环形队列中取出数据,每取出一个数据之后打印"consumer done!"。
void *consumer(void *ring_queue)
{
  ringQueue *rq=(ringQueue*)ring_queue;
  while(true)
  {
    //sleep(1);
    int data=1;
    rq->Get(data);
    cout<<"consumer done# "<<data<<endl;
  }
}
//向环形队列中放入数据,每放入一次数据之后打印"productor done!"。
void *productor(void *ring_queue)
{
  ringQueue *rq=(ringQueue*)ring_queue;
  int count=0;
  while(true)
  {
    sleep(1);
    rq->Put(count);
    count++;
    if(count==9)
    {
      count=0;
    }
    cout<<"productor done!"<<endl;
  }
}
int main()
{
  pthread_t con,pro;
  ringQueue *rq=new ringQueue();

  pthread_create(&con,nullptr,consumer,rq);
  pthread_create(&pro,nullptr,productor,rq);

  pthread_join(con,nullptr);
  pthread_join(pro,nullptr);

  return 0;
}

在这里插入图片描述

线程池

1.什么是线程池

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

2.使用场景

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。如:WEB服务器完成网页请求这样的任务。因为单个任务小,而任务数量巨大。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

3.线程池简单代码实现

#pragma once
#include<iostream>
#include<queue>
#include<unistd.h>
#include<pthread.h>
#include<math.h>
using namespace std;
#define NUM 5
//创建线程将要实现任务,这里为计算一个数的平方;一个任务便是一个线程池中的线程
class Task
{
public:
  int num;
public:
  Task(){}
  Task(int n):num(n)
  {}
  void Run()
  {
    cout<<"thread id ["<<pthread_self()<<"]"<<"task run ! num "<<num<<" pow is "<<pow(num,2)<<endl;
  }
  ~Task()
  {}
};
//线程池
class threadPool
{
private:
  queue<Task*> q;
  int max_num;
  pthread_mutex_t lock;
  pthread_cond_t cond;//指让消费者操作
  //static bool quit;
public:
  //加锁
  void LockQueue()
  {
    pthread_mutex_lock(&lock);
  }
  //解锁
  void UnlockQueue()
  {
    pthread_mutex_unlock(&lock);
  }
  bool IsEmpty()
  {
    return q.size()==0;
  }
  //线程等待
  void ThreadWait()
  {
    pthread_cond_wait(&cond,&lock);
  }
  //唤醒线程
  void ThreadWakeUp()
  {
    pthread_cond_signal(&cond);
  }
public:
  threadPool(int _max=NUM):max_num(_max)
  {}
  //实现单个线程的运行
  static void *Rontine(void *arg)
  {
    threadPool *this_p=(threadPool*)arg;
    while(true)
    {
      this_p->LockQueue();
      while(this_p->IsEmpty())
      {
        this_p->ThreadWait();
      }
      Task t;
      this_p->Get(t);
      this_p->UnlockQueue();
      t.Run();
    }
  }
  //线程池初始化
  void ThreadPoolInit()
  {
  	//初始化锁与等待条件
    pthread_mutex_init(&lock,nullptr);
    pthread_cond_init(&cond,nullptr);
    pthread_t t;
    //线程池队列未满便一直放入Task任务并运行
    for(int i=0;i<max_num;i++)
    {
      pthread_create(&t,nullptr,Rontine,this);
    }
  }
  //在线程池队列中放入任务
  void Put(Task &in)
  {
    LockQueue();
    q.push(&in);
    UnlockQueue();
    ThreadWakeUp();
  }
  //取出并执行任务
  void Get(Task &out)
  {
    Task*t=q.front();
    q.pop();
    out=*t;
  }
  ~threadPool()
  {
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);
  }
};

#include "threadPool.h"
using namespace std;

int main()
{
  threadPool *tp = new threadPool();
  tp->ThreadPoolInit();
  while(true)
  {
    sleep(1);
    int x=rand()%10+1;
    Task t(x);
    tp->Put(t);
  }
  return 0;
}

运行截图:下图中可见很多不同的线程在一起运行,thread id是不同的
在这里插入图片描述

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值