线程池处理任务

4 篇文章 0 订阅

一、线程池

1.概念

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

2.模拟实现

ThreadPool.hpp

#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>

#define NUM 5
template <typename T>
class ThreadPool
{
private:
	int num;
	std::queue<T> task_queue;  //任务队列,临界资源
	pthread_mutex_t lock; //对临界资源进行保护
	pthread_cond_t cond;  //有休眠就有唤醒
public:
	ThreadPool(int _num = NUM):thread_num(_num)
	{
		pthread_mutex_init(&lock);  //对锁初始化
		pthread_cond_init(&cond,nullptr);
	}
	void LockQueue()
	{
		pthread_mutex_lock(&lock);  //加锁
	}
	void UnlockQueue()
	{
		pthread_mutex_unlock(&lock);  //解锁
	}
	bool IsQueueEmpty()
	{
		return task_queue.size() == 0 ? true : false; 
	}
	void Wait()
	{
		pthread_cond_wait(&cond,&lock);
	}
	void WakeUp()
	{
		pthread_cond_signal(&cond);
	}
	static void *Routine(void *arg)  //static:这个方法属于类,不属于对象,只会传入所想要传入的参数,不会有this指针
	{
		pthread_detach(pthread_self());
		ThreadPool *self = (ThreadPool*)arg; 
		while(true)
		{
			self->LockQueue();  //为了访问非静态成员函数 
			while(self()->IsQueueEmpty())
			{
				//wait
				self->Wait();
			}
			//任务队列一定有队列
			T t;
			self->Pop();  //拿任务
			self->UnlockQueue();
			
			//处理任务
			t.Run();
		}
	}
	void InitThreadPool()
	{
		
	    pthread_t tid;
		for(int i = 0; i < thread_num; i++)
		{
			pthread_create(&tid,nullptr,Routine,this);
			pthread_detach(tid);	
		}
	}
	void Push(const T& in)
	{
		LockQueue();
		task_queue.push(in);
		UnlockQueue();

		WakeUp();
	}
	void Pop(T &out)
	{
		out = task_queue.front();
		task_queue.pop();
	}
	~ThreadPool()
	{
		pthread_cond_destroy(&cond); 
		pthread_mutex_destroy(&lock);
	}
}

task.hpp

#pragma once
#include<iostream>
#include<pthread.h>
//typedef int (*handler_t)(int,int,char);  //回调函数

class Task
{
private:
	int x;
	int y;
	char op;
	//handler_t handler;
public:
	Task()
	{}
	Task(int _x,int _y,chat _op):x(_x),y(_y),op(_op)
	{
		
	}
	void Run()
	{
		//任务处理
		//int ret = handler(x,y,op);
		int z = 0;
		swotch(op)
		{
			case '+':
					z = x + y;
					break;
			case '-':
					z = x - y;
					break;
			case '*':
					z = x * y;
					break;
			case '/':
					if(y == 0) std::cerr << "div zero! " << std::endl;
					if(y != 0)
						z = x / y;
					break;
			case '%':
					if(y == 0) std::cerr << "mod zero! " << std::endl;
					if(y != 0)
						z = x % y;
					break;
			default:
					perror("operator error!\n");
					break;
		} 
		std::cout << "thread:[" << pthread_self() << "]:" << x << op << y << "="<< z << std::endl;
	}
	~Task()
	{}
}

main.cc

#include"Task.hpp"
#include"ThreadPool.hpp"
#include<cstdlib>
#include<ctime>
#include<unistd.h>

int main()
{
	ThreadPool<Task> *tp = new ThreadPool<Task>();
	tp->InitThreadPool();

	srand((unsigned long)time(nullptr));
	const char *op = "+-*/%";
	while(true)
	{
		int x = rand()%100+1;  //[0.100]
		int y = rand()%100+1;
		Task t(x,y,op[x%5]);  //x%5对应0-4

		tp->Push(t);  //任务塞队列
		sleep(1);
	}
	return 0;
}

二、线程安全的单例模式

1.设计模式

对于常见的场景,给定对应的解决方案

2.单例模式

(1)特点
某些类,只应该具有一个对象(实例),就称之为单例
(2)实现方式
饿汉实现方式

template <typename T>
class Singleton
{
	static T data;  //对象已经存在
public:
	static T* GetInstance()
	{
		return &data; 
	}
};

懒汉实现方式核心思想:“延时加载”,从而优化服务器的启动速度

template <typename T>
class Singleton
{
	static T* inst;
public:
	static T* GetInstance()
	{
		if(inst == NULL)
		{
			inst = new T();
		}
		return inst;
	}
};

存在问题:线程不安全,第一次调用Getinstance的时候,如果两个线程同时调用,可能会创建两份T对象的实例,但是后续再次调用,就没有问题了
改进:线程安全版本

template <typename>
class Singleton
{
	volatile static T* inst;  //设置volatile,否则可能会被编译器优化
	static std::mutex lock;
public:
	static T* GetInstance()
	{
		if(inst == NULL) //双重判定空指针,降低锁冲突的概率,提高性能
			lock.lock();   //使用互斥锁,保证多线程情况下也只能调用一次
			{
				if(inst == NULL)
				{
					inst = new T();
				}
				lock.unlock();
			}
			return inst;
	}
};

注意:
(1)加锁解锁的位置
(2)双重if判定,避免不必要的锁竞争
(3)volatile关键字防止过度优化

三、读写锁

1.主要处理多读少写的情况

三种关系:读者和读者(没有冲突),写者和写者(互斥关系),读者和写者(互斥,同步)
读和写
一个交易场所

2.应用场景

(1)数据写下之后,剩下操作就是读取
(2)写的操作少,读操作多

3.读者,按照读的方式加锁;写者,按照写的方式加锁,读写接口

(1)设置读写优先

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref); 
/*pref 共有 3 种选择 PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况 PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和 PTHREAD_RWLOCK_PREFER_READER_NP 一致 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁 */

(2)初始化

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);

(3)销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

(4)加锁和解锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); 
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

多线程情况下,读写同时到来,如果还有人在读,接下来的读的角色,不要进入临界区进行读,等写着进入,并写完,然后你再读(写者优先);反之就是读者优先

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值