一.半同步/半反应堆模型简介
该模型不同于前文提到的半同步/半反应堆模型半同步/半反应堆模型以及简单实现(一)
1. one thread one loop。主线程和每个工作线程之间都维持自己的事件循环,它们各自监听和处理不同的事件。
2. 主线程只管理监听socket,连接socket由工作线程来处理。当有新的连接到来时,主线程就接受并将新返回的socket派发给某个工作线程,此后该socket上的任何I/O操作都由被选中的工作线程来处理。
3. 主线程接受连接socket后,向工作线程的任务队列中插入一个任务,并且发送管道消息。工作线程检测到有管道消息时,则判断是否为新连接请求。若为新连接请求,则将socket注册到工作线程的事件循环中。
二.半同步/半反应堆模型实现
(以下部分实现了一个线程池轮子,之后的项目可以继承Task类并且override init()函数来重复使用该轮子)
为了避免重复造轮子,使用libevent库
线程类:
Thread.h
//
// Created by Administrator on 2022/7/14 0014.
//
#ifndef LIBEVENT_THREADPOOL_THREAD_INTERNAL_H
#define LIBEVENT_THREADPOOL_THREAD_INTERNAL_H
#include <event2/event.h>
#include <memory>
#include <mutex>
#include <list>
class Task;
class Thread {
public:
// 构造函数
Thread() = default;
// 析构函数
~Thread() = default;
// 线程运行
void start();
// 线程设置
void set_up();
// 线程主函数
void thread_main();
// 激活线程
void active();
// 添加任务
void add_task(std::shared_ptr<Task> task);
// 事件回调函数
static void event_callback(evutil_socket_t sock, short what, void *ctx);
// 设置线程id
void set_thread_id(int thread_i) {
this->thread_id = thread_i;
}
// 获取线程id
int get_thread_id() const {
return thread_id;
}
private:
// 线程id
int thread_id;
// 事件循环上下文
std::shared_ptr<event_base> base;
// 管道写端口
int notify_fd;
// 任务队列锁
std::mutex _mutex;
// 任务队列
std::list<std::shared_ptr<Task>> work_queue;
};
#endif //LIBEVENT_THREADPOOL_THREAD_INTERNAL_H
Thread.cpp
//
// Created by Administrator on 2022/7/14 0014.
//
#include "Thread.h"
#include <thread>
#include <unistd.h>
#include <iostream>
#include <event2/event.h>
#include "Task.h"
void Thread::start() {
// 线程设置
set_up();
// 进入线程主函数
std::thread th(&Thread::thread_main, this);
// 线程分离
th.detach();
}
void Thread::set_up() {
// 初始化管道
int fds[2];
if (pipe(fds)) {
std::cerr << "pipe() error" << std::endl;
return;
}
// 写端保存
notify_fd = fds[1];
// 事件循环上下文配置
std::shared_ptr<event_config> ev_config(event_config_new(), event_config_free);
event_config_set_flag(ev_config.get(), EVENT_BASE_FLAG_NOLOCK);
// 创建事件循环上下文
std::shared_ptr<event_base> temp_base(event_base_new_with_config(ev_config.get()), event_base_free);
base = temp_base;
// 创建监听事件
auto *ev = event_new(base.get(), fds[0], EV_READ | EV_PERSIST, event_callback, this);
event_add(ev, nullptr);
}
void Thread::thread_main() {
std::cout << "thread " << thread_id << " start" << std::endl;
// 开启时间循环
event_base_dispatch(base.get());
std::cout << "thread " << thread_id << " end" << std::endl;
}
void Thread::active() {
ssize_t len = write(notify_fd, "c", 1);
if (len <= 0) {
std::cerr << "active() error" << std::endl;
return ;
}
}
void Thread::add_task(std::shared_ptr<Task> task) {
task->set_event_base(base);
_mutex.lock();
work_queue.push_back(task);
_mutex.unlock();
}
void Thread::event_callback(evutil_socket_t sock, short what, void *ctx) {
auto *thread = (Thread *)ctx;
char buffer[2] = {0};
ssize_t len = read(sock, buffer, sizeof(buffer) - 1);
if (len <= 0) {
return ;
}
thread->_mutex.lock();
if (thread->work_queue.empty()) {
thread->_mutex.unlock();
return;
}
std::shared_ptr<Task> tk = thread->work_queue.front();
thread->work_queue.pop_front();
thread->_mutex.unlock();
tk->init();
}
start()函数: start()函数主要设置该线程参数和启动线程
set_up()函数: set_up()函数中初始化线程管道和创建了事件循环上下文环境base,因为base不涉及多线程访问,所以将base设置为无锁。
thread_main()函数: thread_main()函数为线程入口函数,线程阻塞在事件循环中,若有事件发生则唤醒该线程。
active()函数: active()函数为激活线程函数,该函数向管道中发送“c”字符(参考memcached),管道读端口检测到可读后该线程事件循环会跳出,并且执行相应的回调函数event_callback()。
add_task()函数: add_task()函数为向线程的任务队列中添加任务。
event_callback()函数: event_callback()函数为管道读端口发生事件时对应的回调函数,首先从任务队列中取出一个任务,然后执行该任务的init()函数。
线程池类:
ThreadPool.h
//
// Created by Administrator on 2022/7/14 0014.
//
#ifndef LIBEVENT_THREADPOOL_THREADPOOL_INTERNAL_H
#define LIBEVENT_THREADPOOL_THREADPOOL_INTERNAL_H
#include <vector>
#include <memory>
class Thread;
class Task;
class ThreadPool {
public:
// 单例模式对象
static ThreadPool *Instance();
// 初始化
void init(int thread_count = 4);
// 添加任务
void dispatch(std::shared_ptr<Task> task);
private:
// 上一次执行任务线程
int last_thread_id = -1;
// 线程数量
int thread_count;
// 线程数组
std::vector<std::shared_ptr<Thread>> threads;
// 构造函数
ThreadPool() = default;
// 析构函数
~ThreadPool() = default;
};
#endif //LIBEVENT_THREADPOOL_THREADPOOL_INTERNAL_H
ThreadPool.cpp
//
// Created by Administrator on 2022/7/14 0014.
//
#include "ThreadPool.h"
#include "Thread.h"
#include "Task.h"
#include <iostream>
#include <thread>
ThreadPool *ThreadPool::Instance() {
static ThreadPool threadPool;
return &threadPool;
}
void ThreadPool::init(int thread_count) {
this->thread_count = thread_count;
for (int i = 0; i < thread_count; i++) {
// 线程创建并加入线程数组
threads.emplace_back(new Thread());
// 打印信息
std::cout << "create " << i + 1 << "th thread" << std::endl;
// 初始化线程id
threads[i]->set_thread_id(i + 1);
// 线程启动
threads[i]->start();
// 睡眠10ms
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void ThreadPool::dispatch(std::shared_ptr<Task> task) {
int thread_id = (++last_thread_id) % thread_count;
task->set_thread_id(thread_id);
threads[thread_id]->add_task(task);
threads[thread_id]->active();
}
线程池类采用了单例模式,在程序运行过程中,有且仅有一个ThreadPool对象。
init()函数:init()函数为初始化线程池,创建出线程对象后对线程进行启动。
dispatch()函数: dispatch()函数为任务分配函数,执行该函数完成对任务对象的分配(此处分配并没有考虑负载均衡问题,后续可能将负载均衡问题加入考虑范围),将任务分配到对应线程的工作队列中并且激活对应的线程。
Task类:
Task.h
//
// Created by Administrator on 2022/7/14 0014.
//
#ifndef LIBEVENT_THREADPOOL_TASK_H
#define LIBEVENT_THREADPOOL_TASK_H
#include <memory>
#include <event2/event.h>
class Task {
public:
// 构造函数
Task() = default;
// 虚析构函数
virtual ~Task() = default;
// 任务初始化
virtual void init() = 0;
// 设置线程id
void set_thread_id(int thread_id);
// 设置event_base
void set_event_base(std::shared_ptr<event_base> base);
// 设置sock
void set_sock(int sock);
// 获取线程id
int get_thread_id() const;
// 获取event_base()
event_base *get_event_base() const;
// 获取sock
int get_sock() const;
private:
// 任务套接字
int sock;
// 任务所属线程id
int thread_id;
// 任务所属event_base;
std::weak_ptr<event_base> base;
};
#endif //LIBEVENT_THREADPOOL_TASK_H
Task.cpp
//
// Created by Administrator on 2022/7/14 0014.
//
#include "Task.h"
void Task::set_thread_id(int thread_id) {
this->thread_id = thread_id;
}
void Task::set_event_base(std::shared_ptr<event_base> base) {
this->base = base;
}
void Task::set_sock(int sock) {
this->sock = sock;
}
int Task::get_sock() const {
return sock;
}
int Task::get_thread_id() const {
return thread_id;
}
event_base *Task::get_event_base() const {
return base.lock().get();
}
Task类中的init()函数为纯虚函数,意味着Task类实际为任务类的父类,根据任务类型的不同创建子类对象override Task类init()函数。
三.半同步/半反应堆模型实现Echo_Server Demo
效果展示:
资源地址: