半同步/半反应堆模型以及简单实现(二)

一.半同步/半反应堆模型简介

 该模型不同于前文提到的半同步/半反应堆模型半同步/半反应堆模型以及简单实现(一)

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

效果展示:

 资源地址:

资源地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值