【Linux高性能服务器编程】两种高性能并发模式剖析——领导者/追随者模式

 16b9d0dfc990426e968798e2f5a7628b.png

hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之两种高性能并发模式介绍,在这篇文章中,你将会学习到高效的创建自己的高性能服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!

希望这篇文章能对你有所帮助9fe07955741149f3aabeb4f503cab15a.png,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!1a2b6b564fe64bee9090c1ca15a449e3.png(注:这章对于高性能服务器的架构非常重要哟!!!)

03d6d5d7168e4ccb946ff0532d6eb8b9.gif         

目录

一. 领导者/追随者模式

1.1 什么是领导者和追随者 

2.2 模式组件构成

2 3 事件处理器和具体事件处理器

2.4 实例代码分析


 

一. 领导者/追随者模式

1.1 什么是领导者和追随者 

        领导者/追随者模式是多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式。在任意时间点,程序都仅有一个领导者线程,它负责监听I/O事件。而其他线程则都是追随者,它们休眠在线程池中等待成为新的领导者。当前的领导者如果检测到IIO事件,首先要从线程池中推选出新的领导者线程,然后处理I/O事件。此时,新的领导者等待新的I/O事件,而原来的领导者则处理I/O 事件,二者实现了并发。

2.2 模式组件构成

         领导者/追随者模式包括以下几个组件:句柄集 ,线程集 , 事件处理集和具体的事件处理器,关系如下图:

       1.句柄集 : 句柄(Handle) 用于表示I/O资源,在Linux下通常就是一个文件描述符。句柄集管理众多句柄,它使用wait _ for _ event方法来监听这些句柄上的I/O事件, 并将其中的就绪事件通知给领导者线程。领导者则调用绑定到Handle上的事件处理器来处理事件。领导者将Handle 和事件处理器绑定是通过调用句柄集中的register _ handle 方法实现的。

       2.线程集  这个组件是所有工作线程(包括领导者线程和追随者线程)的管理者。它负责各线程之间的同步,以及新领导者线程的推选。线程集中的线程在任一时间必处于如下三种状态之一:

        Leader:线程当前处于领导者身份,负责等待句柄集上的I/O 事件。

  Processing: 线程正在处理事件。领导者检测到I/O 事件之后, 可以转移到 Processing  状态来处理该事件,并调用promote _ new _ leader方法推选新的领导者;也可以指定其他追随者来处理事件(Event Handoff), 此时领导者的地位不变。当处于Processing状态的线程处理完事件之后,如果当前线程集中没有领导者,则它将成为新的领导者,否则它就直接转变为追随者。

        Folower:线程当前处于追随者身份,通过调用线程集的join方法等待成为新的领导者,也肯能被当前的领导者指定新的处理任务

这三种关系的转换关系如下:

2 3 事件处理器和具体事件处理器

        事件处理器和具体的事件处理器事件处理器通常包含一个或多个回调函数handle _event。这些回调函数用于处理事件对应的业务逻辑。事件处理器在使用前需要被绑定到某个句柄上,当该句柄上有事件发生时,领导者就执行与之绑定的事件处理器中的回调函数。具体的事件处理器是事件处理器的派生类。它们必须重新实现基类的handle _ event方法, 以处理特定的任务。如图:


 

2.4 实例代码分析

 主线程函数代码:

 // 创建追随者线程
    pthread_t followers[MAX_FOLLOWERS];
    for (int i = 0; i < MAX_FOLLOWERS; ++i) {
        if (pthread_create(&followers[i], NULL, follower_thread, (void *)(long)server_sock) != 0) {
            perror("pthread_create");
            close(server_sock);
            exit(EXIT_FAILURE);
        }
    }

    // 主线程作为领导者
    while (1) {
        // 获取领导权
        pthread_mutex_lock(&leader_mutex);

        // 等待客户端连接
        client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_sock == -1) {
            perror("accept");
            continue;
        }

        // 通知一个追随者线程处理客户端请求
        pthread_cond_signal(&leader_cond);

        // 释放领导权
        pthread_mutex_unlock(&leader_mutex);
    }

 首先我们创建一个工作线程数组,然后对每个线程进行初始化(创建),之后进入主线程,首先对主线程即领导者上锁,然后开始等待客户端的连接,如果有连接,则接收客户端套接字后使用pthread_cond_signal()函数通知一个追随者线程处理客户端请求,之后对此线程解锁。

  注:pthread_cond_signal()函数:

pthread_cond_signal(&leader_cond); 是一个 POSIX 线程(pthread)函数,用于在多线程编程中进行条件同步。这个函数的作用是唤醒至少一个等待在指定条件变量 leader_cond 上的线程。

在领导者/追随者模式中,条件变量用于协调领导者线程和追随者线程的工作。当领导者线程接受了一个新的客户端连接后,它需要通知一个追随者线程来处理这个连接。这时,领导者线程会调用 pthread_cond_signal 来唤醒一个正在等待的追随者线程。

具体来说,pthread_cond_signal 做了以下几件事情:

  1. 如果有追随者线程正在 leader_cond 条件变量上等待(通过 pthread_cond_wait 或 pthread_cond_timedwait),pthread_cond_signal 会唤醒其中一个线程。

  2. 唤醒的线程将从 pthread_cond_wait 或 pthread_cond_timedwait 函数返回,并且该线程在继续执行之前必须重新获取与条件变量相关联的互斥锁(在本例中是 leader_mutex)。

  3. 如果没有线程在条件变量上等待,pthread_cond_signal 的调用不会有任何效果。

在领导者/追随者模式中,pthread_cond_signal 是一个关键点,因为它确保了只有一个追随者线程被唤醒来处理客户端连接,从而避免了多个线程同时处理同一个连接的问题。这是通过在领导者线程和追随者线程之间共享一个互斥锁来实现的,只有当领导者线程释放了互斥锁并且发出了信号之后,追随者线程才能够继续执行。

追随者线程处理函数代码:

// 追随者线程函数
void *follower_thread(void *arg) {
    int client_sock;
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);

    while (1) {
        // 获取领导权
        pthread_mutex_lock(&leader_mutex);

        // 等待成为领导者
        pthread_cond_wait(&leader_cond, &leader_mutex);

        // 接受客户端连接
        client_sock = accept((int)arg, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_sock == -1) {
            perror("accept");
            continue;
        }

        // 处理客户端请求
        char buffer[1024];
        int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
        if (bytes_received > 0) {
            buffer[bytes_received] = '\0';
            printf("Received data: %s\n", buffer);
            send(client_sock, "Message received.\n", 18, 0);
        }

        // 关闭客户端连接
        close(client_sock);

        // 释放领导权
        pthread_mutex_unlock(&leader_mutex);
    }

    return NULL;
}

       在代码中,主线程接收到新的客户端连接后,会通知一个等待的追随者线程(通过pthread_cond_signal函数),并将服务器套接字文件描述符(server_sock)作为参数传递给追随者线程。然而,这个服务器套接字文件描述符只是用于监听新的连接请求,并不用于与客户端进行数据传输。因此,当追随者线程被唤醒并开始执行时,它需要通过accept函数再次接收客户端连接,以获取一个用于与客户端进行数据传输的新套接字文件描述符(client_sock)。这个新的套接字文件描述符才是用于与客户端进行数据传输的,而原始的服务器套接字文件描述符(server_sock)仍然由主线程使用,用于接收新的连接请求。

注意哦:

在这段代码中,pthread_mutex_lock(&leader_mutex); 的作用是获取互斥锁,确保同一时刻只有一个线程能够执行接下来的关键代码段。这个关键代码段包括等待成为领导者的条件变量 pthread_cond_wait(&leader_cond, &leader_mutex); 和处理客户端请求的代码。

当主线程调用 pthread_mutex_lock(&leader_mutex); 时,它获取了互斥锁,这意味着其他尝试获取同一互斥锁的线程将会被阻塞,直到主线程释放这个锁。在主线程释放锁之前,其他线程不能进入关键代码段。

在主线程中,当一个新的客户端连接被接受后,主线程会通过 pthread_cond_signal(&leader_cond); 唤醒一个等待的追随者线程。这个信号只唤醒一个线程,而不是所有等待的线程。被唤醒的线程将有机会获取互斥锁并成为领导者,然后开始处理客户端请求。

一旦一个追随者线程被唤醒并开始处理客户端请求,它会保持互斥锁,直到请求处理完毕。在这个过程中,其他追随者线程仍然处于等待状态,因为它们无法获取互斥锁。当领导者线程处理完请求并释放互斥锁后,其他线程中的一个将有机会被唤醒并成为新的领导者。

这种设计确保了同一时刻只有一个线程能够处理客户端请求,从而避免了多个线程同时处理同一个客户端连接的问题。每个线程在处理请求之前都会尝试获取互斥锁,只有成功获取锁的线程才能成为领导者并处理请求。其他线程则在等待获取锁的过程中阻塞。

  好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦4d7d9707063b4d9c90ac2bca034b5705.png!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!2cd0d6ee4ef84605933ed7c04d71cfef.jpeg       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/571397.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【行为型模式】中介者模式

一、中介者模式概述 中介者模式定义&#xff1a;用一个中介对象来封装一系列的对象交互&#xff0c;中介者使各对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式。(对象行为型模式) 中介者模式…

Web3与物联网:探索区块链如何驱动智能设备的未来

引言 在数字化快速发展的时代&#xff0c;Web3技术和物联网&#xff08;IoT&#xff09;都成为了前沿技术的代表。两者的结合正逐渐展现出无限的可能性&#xff0c;尤其是在智能设备和数据安全方面。本文将深入探讨Web3如何与物联网相结合&#xff0c;以及这种结合对未来智能设…

有效三角形的个数 ---- 双指针

题目链接 题目: 分析: 这道题的意思就是将数组的元素, 拿出三个数, 能构成三角形就是有效的判断是否能构成三角形的条件: 两边之和大于第三边, 我们只需找到三个数中最小的两个数之和是否大于第三边, 大于则可以构成三角形解法一: 暴力解法, 即找到所有的三元组, 并挨个判断,…

分布式与一致性协议之CAP(二)

CAP CAP不可能三角 CAP不可能三角是指对于一个分布式系统而言&#xff0c;一致性、可用性、分区容错性指标不可兼得&#xff0c;只能从中选择两个&#xff0c; 如图所示。CAP不可能三角最初是埃里克布鲁尔(Eric Brewer)基于自己的工程实践提出的一个猜想&#xff0c;后被塞斯吉…

【C语言 |预处理指令】预处理指令详解(包括编译与链接)

目录 一、编译与链接 1.翻译环境 -预处理 -编译 -汇编 -链接 2.执行环境 二、预定义符号 三、#define定义常量 四、#define定义宏 五、带有副作用的宏参数 六、宏替换的规则 七、 宏函数的对比 八、#和## 1.#运算符 2.##运算符 九、命名约定 十、#undef 十一、 命…

【03-掌握Scikit-learn:深入机器学习的实用技术】

文章目录 前言数据预处理缺失值处理数据缩放特征选择模型训练参数调整模型评估总结前言 经过了对Python和Scikit-learn的基础安装及简单应用,我们现在将更深入地探究Scikit-learn的实用技术,以进一步提升我们的数据科学技能。在本文中,我们将涵盖数据预处理、特征选择、模型…

【唯美情侣爱情表白纪念HTML单页】

唯美情侣爱情表白纪念HTML单页 效果图部分代码领取代码下期更新预报 效果图 整图 背景图 部分代码 index.html <!DOCTYPE html> <html lang"en"><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"…

YOLOv8 实现车牌检测,生成可视化检测视频(20240424)

原项目源码地址&#xff1a;GitHub 我的源码地址&#xff1a;Gitee 环境搭建请参考&#xff1a;Win10 搭建 YOLOv8 运行环境&#xff08;20240423&#xff09;-CSDN博客 环境测试请参考&#xff1a;本地运行测试 YOLOv8&#xff08;20240423&#xff09;-CSDN博客 训练数据…

《系统架构设计师教程(第2版)》第15章-面向服务架构设计理论与实践-05-SOA设计模式

文章目录 1. 服务注册表模式1.1 服务注册表1.2 SOA治理功能1.3 注册表中的配置文件 2. 企业服务总线&#xff08;ESB&#xff09;模式3. Synchro ESB3. 微服务模式3.1 概述3.2 微服务架构模式方案3.2.1 聚合器微服务1&#xff09;概述2&#xff09;几种特殊的聚合微服务 3.2.2 …

RTT学习 cortex-m移植

Cortex-M移植 PRIMASK寄存器 PRIMASK寄存器为1位宽的中断屏蔽寄存器。在置位时&#xff0c;它会阻止不可屏蔽中断&#xff08;NMI&#xff09;和HardFault异常之外的所有异常&#xff08;包括中断&#xff09;。实际上&#xff0c;它是将当前异常优先级提升为0&#xff0c;这也…

Jenkins CI/CD 持续集成专题四 Jenkins服务器IP更换

一、查看brew 的 services brew services list 二、编辑 homebrew.mxcl.jenkins-lts.plist 将下面的httpListenAddress值修改为自己的ip 服务器&#xff0c;这里我是用的本机的ip 三 、重新启动 jenkins-lts brew services restart jenkins-lts 四 、浏览器访问 http://10.…

golang学习笔记(defer基础知识)

什么是defer defer语句用于golang程序中延迟函数的调用&#xff0c; 每次defer都会把一个函数压入栈中&#xff0c; 函数返回前再把延迟的函数取出并执行。 为了方便描述&#xff0c; 我们把创建defer的函数称为主函数&#xff0c; defer语句后面的函数称为延迟函数。延迟函数…

【Burpsuite靶场】XSS专题精讲

【个人】&#xff1a;NEUQ大一学生 【专业】&#xff1a;通信工程 (Communication Engineering) 【个人方向】&#xff1a;网安、开发双管齐下 【座右铭】&#xff1a;真正的英雄主义,就是看清生活的真相后依然热爱生活 -- 罗曼.罗兰 一、认识XSS&#xff08;跨站脚本攻击&…

fatal: unable to access ‘https://github.com/alibaba/flutter_boost.git/

Git error. Command: git fetch stdout: stderr: fatal: unable to access ‘https://github.com/alibaba/flutter_boost.git/’: Failed to connect to github.com port 443 after 75005 ms: Couldn’t connect to server exit code: 128 GitHub (国际型)代码 分发平台/托管平…

梯度下降法总是在同一点收敛吗?

梯度下降法总是在同一点收敛吗&#xff1f; 梯度下降法并不总是在同一点收敛。梯度下降法的收敛取决于多个因素&#xff0c;包括初始参数的选择、学习率的设置、损失函数的形状等。 以下是一些影响梯度下降法收敛行为的关键因素&#xff1a; 1.初始参数&#xff1a; 初始参数…

Json-server 模拟后端接口

json-server&#xff0c;模拟rest接口&#xff0c;自动生成增删改查接口。(官网地址&#xff1a;json-server - npm) 使用方法&#xff1a; 1. 安装json-server&#xff0c;npm i json-server -g 2. 创建json文件&#xff0c;文件中存储list数据&#xff0c;db.json {"…

图像超分辨率技术在AI去衣中的应用探索

在数字图像处理领域&#xff0c;图像超分辨率&#xff08;Super-Resolution, SR&#xff09;技术一直是研究的热点之一。该技术旨在从低分辨率的图像中恢复出高分辨率的图像&#xff0c;以提供更清晰、更丰富的细节信息。近年来&#xff0c;随着人工智能&#xff08;AI&#xf…

<计算机网络自顶向下> 路由器组成

路由器结构概况 路由&#xff1a;运行路由选择算法/协议&#xff08;RIP, OSPF, BGP&#xff09;生成路由表转发&#xff1a;从输入到输出链路交换数据包-根据路由表进行分组的转发中间的fabric是用来接收输入的分组交给输出端口的&#xff0c;完成局部的转发&#xff08;根据…

free 命令示例

目录 ⛳️推荐 前言 Linux 中如何使用 free 命令 1、以人类可读的形式显示信息 2、连续显示统计数据 3、定义显示统计数据的次数 4、指定输出数据类型 5、获取物理内存和交换内存的总和 总结 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&am…

掌握注册唤起应用的秘诀,Xinstall助你提升用户体验

在移动互联网时代&#xff0c;App已经成为我们日常生活中不可或缺的一部分。然而&#xff0c;随着App数量的激增&#xff0c;如何让自己的App在激烈的市场竞争中脱颖而出&#xff0c;成为开发者们关注的焦点。其中&#xff0c;注册唤起应用作为提升用户体验和转化率的关键环节&…