基于多反应堆的高并发服务器【C/C++/Reactor】(中)在EventLoop的任务队列中添加新任务

  1. 任务队列是一个链表,每个节点包含channel类型文件描述符操作类型
  2. 在添加节点时,需要考虑线程同步,并确保节点被正确地添加到链表中
  3. 节点的操作可以写到另一个函数中,以便于程序的维护。
  4. 在添加任务节点时,需要加互斥锁,因为有可能是当前线程主线程进行添加操作。

本文主要介绍了在dispatcher中的处理流程,包括线程同步以及节点的添加和操作。其中,任务队列是一个链表,每个节点包含channel类型、文件描述符和操作类型。在添加节点时,需要考虑线程同步,并确保节点被正确地添加到链表中。

  • EventLoop.h
// 添加任务到任务队列
int eventLoopAddTask(struct EventLoop* evLoop,struct Channel* channel,int type);
  • EventLoop.c

(1) 为什么在上面添加链表节点的时候需要加互斥锁

  • 因为有可能是当前线程去添加,也有可能是主线程去添加。

(2) 如果当前的线程是主线程,那么我们能够让主线程进行节点的处理

  • 肯定不能,因为你当前主线程它只能负责和客户端建立连接,如果这个连接建立好了,剩下的事情都是需要由这个子线程来完成的。所以主线程肯定不会给你去处理任务队列里边的节点。在主线程里边,其实它是有一个反应堆模型的,在当前的这个子线程里边也有一个反应堆模型。每个反应堆模型里边都有一个Dispatcher。关于这个Dispatcher就是epoll、poll、或者select模型,所以主线程去处理的话,这个任务就放到主线程的那个Dispatcher里边了,这样很显然是不对的。故在子线程的任务队列里边有了任务之后,还需要交给子线程的Dispatcher去处理。因此这个节点的处理,还需要判断当前线程到底是什么线程。
  • 如果它是主线程不能让它去处理,如果是子线程,直接让它去处理。 
// 添加任务到任务队列
int eventLoopAddTask(struct EventLoop* evLoop,struct Channel* channel,int type) {
    // 加锁,保护共享资源
    pthread_mutex_lock(&evLoop->mutex);
    // 创建新节点,后添加到任务队列中去
    struct ChannelElement* node = (struct ChannelElement*)malloc(sizeof(struct ChannelElement));
    node->channel = channel;
    node->type = type;
    node->next = NULL;
    // 链表为空
    if(evLoop->head == NULL) {
        evLoop->head = evLoop->tail = node;
    }else {
        evLoop->tail->next = node; // 添加
        evLoop->tail = node; // 后移
    }
    pthread_mutex_unlock(&evLoop->mutex);
    // 处理节点
    /**
     * 这个描述假设了一个前提条件,就是当前的EventLoop反应堆属于子线程
     * 细节:
     *  1.对于链表节点的添加:可能是当前线程也可能是其他线程(主线程)
     *      1).修改fd的事件,当前子线程发起,当前子线程处理
     *      2).添加新的fd(意味着和一个新的客户端建立连接,这是由主线程做的,故添加任务节点这个操作肯定是由主线程做的),
     *          添加任务节点的操作是由主线程发起的
     *  2.不能让主线程处理任务队列里边的节点,需要由当前的子线程去处理
    */
    if(evLoop->threadID == pthread_self()) {
        // 当前子线程
        (待续写)...
    }else{
        // 主线程 -- 告诉子线程处理任务队列中的任务
        // 1.子线程在工作 2.子线程被阻塞了:select、poll、epoll
        (待续写)...
    }
    return 0;
}

一、任务队列与文件描述符

  • dispatcher中,存在一个带检测的文件描述符集合。任务队列用于修改这个集合,例如添加、删除或修改。任务队列实际上是一个链表,每个节点代表一个channel元素。每个节点包含三个成员:channel类型、文件描述符和操作类型

二、任务队列的线程同步

  • 由于任务队列可能被多个线程访问,因此在操作任务队列时需要进行线程同步
  • 这意味着在添加新节点到任务队列时,需要确保线程安全

三、节点的添加与操作

  • 节点添加:当链表为空时,直接将新节点设置为任务队列的头;当链表不为空时,将新节点添加到链表的尾部(即tail节点的后边),并更新tail节点的next指针。
  • 节点操作:将节点的处理操作移至另一个函数中,以便于程序的维护

四、线程角色与任务处理

  • 主线程子线程的角色:添加任务节点可能是当前线程或主线程的操作。
  • 主线程负责与新客户端建立连接,而子线程处理其他任务
  • 主线程与子线程的任务分配:主线程不会处理任务队列中的节点,而是通知子线程去处理

五、总结

  • dispatcher中的处理流程,包括任务队列用于修改一个带检测的文件描述符集合、线程同步以及节点的添加和操作。为了确保程序的稳定性和正确性,我们需要关注线程安全和任务分配,确保每个线程都能在其角色范围内高效地工作。