同步与异步,阻塞与非阻塞
- 同步:调用一旦开始,调用者必须等到调用结果返回,才能继续执行后续的行为。
- 异步:调用开始后会立即返回消息,调用者继续执行后续的操作,更像是一个消息传递。
- 阻塞: 调用结果返回之前,当前线程会挂起,调用线程只有在得到返回结果之后才会返回。
- 非阻塞:调用线程在不能立刻得到结果的期间,该调用不会阻塞当前线程。
- 阻塞和非阻塞强调的是程序在等待调用结果(消息,返回值)时的状态。
- 同步与异步强调的是消息通信机制。
两种事件处理模式
- reactor模式中,主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话立即通知工作线程(逻辑单元 ),读写数据、接受新连接及处理客户请求均在工作线程中完成。通常由同步I/O实现。
- proactor模式中,主线程和内核负责处理读写数据、接受新连接等I/O操作,工作线程仅负责业务逻辑,如处理客户请求。通常由异步I/O实现。
- 本文使用的是同步I/O模拟的proactor模式,主函数的线程为主线程,线程池中的子线程为工作线程。
- 主线程充当异步线程,负责监听所有socket上的事件
- 若有请求到来,主线程接收并且注册epoll事件
- 如果socket上有读写事件发生,主线程从socket上接受数据,并将数据封装成请求对象插入到请求队列中
- 线程池中的工作线程睡眠在请求队列上,当有任务到来时,工作线程处理客户端的请求与服务器的响应。
线程同步机制类的封装
- 线程池中的线程同步一般配合互斥锁、信号量、条件变量使用
- 互斥锁
线程同步时,对其中一个线程加上互斥量(mutex),其他线程阻塞,直到当前线程释放互斥锁,第一个变为可运行状态的线程可以对互斥锁加锁。 - 条件变量
条件变量用于持有互斥锁的线程满足某种条件时才去执行阻塞或者唤醒线程,避免线程不断的轮询解锁和加锁从而浪费cpu资源,提高了运行效率。 - 信号量
信号量是由内核来维护的,他独立出进程。因此可以通过它来进行同步。对于信号量的工作原理,其实和互斥锁+条件变量相似。本质上,信号量实现了互斥量+条件变量的功能。 - 参考链接1
- 参考链接2
locker.h
#ifndef LOCKER_H
#define LOCKER_H
#include <pthread.h>
#include <exception>
#include <semaphore.h>
//线程同步机制封装类
//互斥锁类
class locker
{
public:
locker()
{
//初始化互斥量
if (pthread_mutex_init(&m_mutex, NULL) != 0)
{
throw std::exception();
}
}
~locker()
{
//释放互斥量资源
pthread_mutex_destroy(&m_mutex);
}
//加锁
bool lock()
{
return pthread_mutex_lock(&m_mutex) == 0;
}
//解锁
bool unlock()
{
return pthread_mutex_unlock(&m_mutex) == 0;
}
//获取互斥量成员信息
pthread_mutex_t *get()
{
return &m_mutex;
}
private:
pthread_mutex_t m_mutex;
};
//条件变量类
//判断队列中是否有数据,如果没有数据就线程阻塞,有数据就继续执行
class cond
{
public:
cond()
{
if (pthread_cond_init(&m_cond, NULL))
{
throw std::exception();
}
}
~cond()
{
pthread_cond_destroy(&m_cond);
}
//条件变量配合互斥量使用 传入一个互斥量参数
bool wait(pthread_mutex_t *mutex)
{
return pthread_cond_wait(&m_cond, mutex) == 0;
}
//等待多长时间,调用了这个函数,线程会阻塞
bool timedwait(pthread_mutex_t *mutex, struct timespec t)
{
return pthread_cond_timedwait(&m_cond, mutex, &t) == 0;
}
//唤醒一个或者多个等待的线程
bool signal()
{
return pthread_cond_signal(&m_cond) == 0;
}
//唤醒所有的等待的线程
bool broadcast()
{
return pthread_cond_broadcast(&m_cond) == 0;
}
private:
pthread_cond_t m_cond;
};
//信号量类
class sem
{
public:
sem()
{
if (sem_init(&m_sem, 0, 0) != 0)
{
throw std::exception();
}
}
sem(int num)
{
if (sem_init(&m_sem, 0, num) != 0)
{
throw std::exception();
}
}
~sem()
{
sem_destroy(&m_sem);
}
//等待信号量,对信号量加锁,调用一次对信号量的值-1,如果值为0,就阻塞
bool wait()
{
return sem_wait(&m_sem);
}
//增加信号量 对信号量解锁,调用一次对信号量的值+1
bool post()
{
return sem_post(&m_sem);
}
private:
sem_t m_sem;
};
#endif
线程池的实现
线程池主要完成:创建多个线程,通过互斥锁和信号量向工作队列中添加请求任务
- 线程类定义为模板类,方便代码的复用
- 定义线程池类的构造函数和析构函数
- 通过线程池对象向工作队列中添加任务
- 线程池启动,循环判断队列中有无任务,如果队列中有任务,取出任务并执行
threadpool.h
#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <pthread.h>
#include <list>
#include "locker.h"
#include <exception>
#include <cstdio>
//线程池类、定义为模板类为了代码的复用,模板参数T就是任务类
template <typename T>
//线程池类的实现
class threadpool
{
public:
//初始化线程数量、最大请求数量
threadpool(int thread_number = 8, int max_requests = 10000);
~threadpool();
//通过线程池对象向工作队列中添加任务
bool append(T *request);
//线程池启动
void run();
private:
//静态的不能访问非静态的,可通过this对象作为worker的参数传入进去,就可以访问threadpool类中的成员
static void *worker(void *arg);
private:
//线程的数量
int m_thread_number;
//线程池数组,大小为线程的数量m_thread_number
pthread_t *m_threads; //使用指针动态的创建数组
//请求队列中最多允许的,等待处理的请求数量
int m_max_requests;
//请求队列使用list保存任务
std::list<T *> m_workqueue;
//请求队列中共享得有互斥锁
//互斥锁
locker m_queuelocker;
//信号量用来判断任务是否需要处理
sem m_queuestat;
//是否结束线程
bool m_stop;
};
template <typename T>
threadpool<T>::threadpool(int thread_number, int max_requests) : m_thread_number(thread_number), m_max_requests(max_requests), m_stop(false), m_threads(NULL)
{
if ((thread_number <= 0) || (max_requests <= 0))
{
throw std::exception();
}
//创建线程池
m_threads = new pthread_t[m_thread_number];
if (!m_threads)
{
throw std::exception();
}
//创建thread_number个线程,并将它们设置为线程脱离
//线程使用完之后自己释放资源
for (int i = 0; i < thread_number; i++)
{
printf("create the %dth thread\n", i);
// C++中worker是一个静态函数
if (pthread_create(m_threads + i, NULL, worker, this) != 0)
{
delete[] m_threads;
throw std::exception();
}
//线程分离
if (pthread_detach(m_threads[i]))
{
delete[] m_threads;
throw std::exception();
}
}
}
template <typename T>
threadpool<T>::~threadpool()
{
delete[] m_threads;
m_stop = true; //是否结束线程
}
//向工作队列中添加任务
template <typename T>
bool threadpool<T>::append(T *request)
{
//加锁
m_queuelocker.lock();
if (m_workqueue.size() > m_max_requests)
{
//大于最大请求数量解锁
m_queuelocker.unlock();
return false;
}
m_workqueue.push_back(request);
m_queuelocker.unlock(); //解锁
m_queuestat.post(); //信号量增加
return true;
}
template <typename T>
void *threadpool<T>::worker(void *arg)
{
threadpool *pool = (threadpool *)arg;
//线程池创建之后,开始启动
pool->run();
return pool;
}
//线程池运行
template <typename T>
void threadpool<T>::run()
{
//当不结束线程时,一直循环
while (!m_stop)
{
m_queuestat.wait(); //判断有无任务可以做
m_queuelocker.lock();
if (m_workqueue.empty())
{
m_queuelocker.unlock();
continue;
}
//说明有任务,从队列中取出任务
T *request = m_workqueue.front();
m_workqueue.pop_front();
m_queuelocker.unlock();
if (!request)
{
continue;
}
// process()任务函数
request->process();
}
}
#endif