基于多反应堆的高并发服务器【C/C++/Reactor】(中)主线程给子线程添加任务以及如何处理该任务

在看此篇文章,建议先看我的往期文章:

基于多反应堆的高并发服务器【C/C++/Reactor】(中)在EventLoop的任务队列中添加新任务-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135346492?spm=1001.2014.3001.5501一、了解socketpair 函数(CodeGeex生成)

Linux 中的 socketpair 函数用于创建一个双向的 socket 连接,通常用于父子进程之间的通信。在上述代码中,socketpair 函数用于初始化一个事件循环(EventLoop )对象,并为其分配一个socket pair,用于接收来自子进程的数据。

socketpair函数的原型为:

cpp 复制代码
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);

其中,family 参数指定套接字家族(如 AF_UNIXAF_INET ),type 参数指定套接字类型(如 SOCK_STREAMSOCK_DGRAM ),protocol 参数指定套接字协议(如 0 ),**sv[2]**参数指向一个整数数组,用于存储创建的文件描述符对。

如果 socketpair 函数返回 0 ,则表示创建成功 ,如果返回 -1 ,则表示创建失败

二、socketpair初始化

socketpair 是一个在Unix-like系统上创建套接字对的函数。它创建的两个套接字描述符(socketpair[0]socketpair[1] )可以用于进程间通信**(IPC)** 。socketpair[1] 的作用如下:

  1. 双向通信socketpair[1] 可以用于与 socketpair[0] 进行全双工通信。这意味着,每一个套接字既可以读也可以写。例如,可以往 socketpair[0] 中写,从 socketpair[1] 中读;或者从socketpair[1] 中写,从 **socketpair[0]**中读。
  2. 阻塞与非阻塞:如果往一个套接字(如 socketpair[0] )中写入后,再从该套接字读时会阻塞,只能在另一个套接字(如socketpair[1])上读成功。
  3. 进程间通信:读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述符socketpair[0]socketpair[1] 是进程共享的,所以读的进程要关闭写描述符,反之,写的进程关闭读描述符。总的来说,socketpair[1] 在进程间通信中扮演着重要的角色,它使得两个进程可以通过套接字进行数据交换。

>>(1)主线程唤醒子线程

  • Channel.h
cpp 复制代码
// 定义函数指针
typedef int(*handleFunc)(void* arg);
 
struct Channel {
    // 文件描述符
    int fd;
    // 事件
    int events;
    // 回调函数
    handleFunc readCallback;// 读回调
    handleFunc writeCallback;// 写回调
    // 回调函数的参数
    void* arg;
};
  • Channel.c
cpp 复制代码
#include "Channel.h"
 
struct Channel* channelInit(int fd, int events, handleFunc readFunc, handleFunc writeFunc, void* arg) {
    struct Channel* channel = (struct Channel*)malloc(sizeof(struct Channel));
    channel->fd = fd;
    channel->events = events;
    channel->readFunc = readFunc;
    channel->writeFunc = writeFunc;
    channel->arg = arg;
    return channel;
}
  • EventLoop.c

evLoop->socketPair[1] 作了封装,得到了一个channel:

cpp 复制代码
struct Channel* channel = channelInit(evLoop->socketPair[1],ReadEvent, 
        readLocalMessage,NULL,evLoop);

那么对于**evLoop->socketpair[1]**这个文件描述符什么时候就能被被激活呢?

  • 通过一个写数据的函数,往socketPair[0] 里边写数据的时候,通过socketPair[1] 就能够接收到数据,此时这个**socketPair[1]**就被激活了,就去调用其读事件。
  • 这么做的原因是:底层的dispatcherpoll、epoll_wait或select ,它们有可能是阻塞的,比如说它们检测的集合里边有文件描述符没有处于激活状态,我们往这个检测集合里安插了一个内线,通过socketPair[0] 去写数据,只要一写数据,对应的poll、epoll_wait或select 能够检测到**socketPair[1]**的读事件被激活了。
  • 如果检测到socketPair[1] 的读事件被激活了,那么poll、epoll_wait或select 阻塞函数就直接被解除阻塞了。它们解除阻塞了,子线程就能够正常工作了。子线程能够正常工作,子线程就能够处理任务队列里边的任务,通过主线程往evLoop->socketPair[0] 发送数据(写数据),void taskWakeup(struct EventLoop* evLoop);
cpp 复制代码
// 写数据
void taskWakeup(struct EventLoop* evLoop) {
    const char* msg = "我是要成为海贼王的男人!";
    write(evLoop->socketPair[0],msg,strlen(msg));
}

此时**socketPair[1]**被激活了,它所对应的读回调就会被调用。这样就能解除子线程的阻塞,让它去处理任务队列里边的任务

cpp 复制代码
// 读数据 读事件回调函数在执行时需要指定参数
int readLocalMessage(void* arg) {
    struct EventLoop* evLoop = (struct EventLoop*)arg;
    char buffer[256];
    int ret = read(evLoop->socketPair[1],buffer,sizeof(buffer));
    if (ret > 0) {
        printf("read msg: %s\n",buffer);
    }
    return 0;
}

>>(2)channel 添加到任务队列

第一步:channel 添加到任务队列 通过调用eventLoopAddTask(evLoop,channel,ADD); 添加到了任务队列

cpp 复制代码
// channel 添加到任务队列
eventLoopAddTask(evLoop, channel,ADD);

第二步:遍历任务队列 ,从中取出每一个节点,根据节点里边的类型对这个节点做操作(待续~,在后续的文章中会讲到eventLoopProcessTask 这个函数),若是type 为添加(ADD),那么就把任务节点添加到任务队列中去

故在下面的这篇文章的基础上,增加一些代码内容:

基于多反应堆的高并发服务器【C/C++/Reactor】(中)EventLoop初始化和启动_一个eventloop 可以有多少个连接-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135225734?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22135225734%22%2C%22source%22%3A%22weixin_41987016%22%7DEventLoop.h 中添加int sockPair[2]; 表示存储本地通信的fd ,通过socketpair初始化

cpp 复制代码
struct EventLoop{
    bool isQuit;// 开关
    struct Dispatcher* dispatcher;
    void* dispatcherData;
    
    // 任务队列
    struct ChannelElement* head;
    struct ChannelElement* tail;

    // 用于存储channel的map
    struct ChannelMap* channelMap;
    
    // 线程ID,Name,mutex
    pthread_t threadID;
    char threadName[32];
    pthread_mutex_t mutex;
    int socketPair[2]; //存储本地通信的fd 通过socketpair初始化
};

EventLoop.c中续写

cpp 复制代码
struct EventLoop* eventLoopInitEx(const char* threadName) {
    struct EventLoop* evLoop = (struct EventLoop*)malloc(sizeof(struct EventLoop));
    evLoop->isQuit = false; // 没有运行
    evLoop->dispatcher = &EpollDispatcher;
    evLoop->dispatcherData = evLoop->dispatcher->init(); 
    
    // 任务队列(链表)
    evLoop->head = evLoop->tail = NULL;

    // 用于存储channel的map
    evLoop->channelMap = channelMapInit(128);

    evLoop->threadId = pthread_self(); // 当前线程ID
    strcpy(evLoop->threadName,threadName == NULL ? "MainThread" : threadName); // 线程的名字
    pthread_mutex_init(&evLoop->mutex, NULL); 
    
    // 已续写
    int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, evLoop->socketPair);
    if(ret == -1) {
        perror("socketpair");
        exit(0);
    }
    // 指定规则:evLoop->socketPair[0] 发送数据,evLoop->socketPair[1]接收数据
    struct Channel* channel = channelInit(evLoop->socketPair[1],ReadEvent, 
        readLocalMessage,NULL,evLoop);
    // channel 添加到任务队列
    eventLoopAddTask(evLoop, channel,ADD);
    return evLoop;
}
相关推荐
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest模块 解析http请求协议
基于多反应堆的·高并发服务器·c/c++/reactor
_snowstorm_10 个月前
Linux学习之网络编程3(高并发服务器)
linux·服务器·学习·高并发服务器·多进程高并发服务器·多线程高并发服务器
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)完整代码
高并发服务器·c/c++/reactor·多反应堆
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpResponse的定义和初始化 以及组织 HttpResponse 响应消息
基于多反应堆的·高并发服务器·c/c++/reactor
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest 提取请求行、解析请求行和优化 以及解析请求头并存储
请求行·请求头·基于多反应堆的·高并发服务器·c/c++/reactor
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)创建一个TcpConnection实例 以及 接收客户端数据
基于多反应堆的·高并发服务器·c/c++/reactor
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)处理任务队列中的任务
删除·基于多反应堆的·高并发服务器·c/c++/reactor·处理任务队列中的任务·添加·修改
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)子线程 WorkerThread的实现 和 线程池ThreadPool的初始化
高并发服务器·c/c++/reactor·多反应堆·workerthread的实现
呵呵哒( ̄▽ ̄)"10 个月前
基于多反应堆的高并发服务器【C/C++/Reactor】(中)在EventLoop中处理被激活的文件描述符的事件
eventloop·高并发服务器·c/c++/reactor·多反应堆·处理被激活的文件描述符的事件