【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       

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot自动配置底层源码分析

文章目录 1. 什么是SpringBoot的自动装配&#xff1f;2. SpringBoot自动装配的底层原理 1. 什么是SpringBoot的自动装配&#xff1f; Spring Boot的自动配置是一种机制&#xff0c;它使得开发者能够快速地开始构建Spring应用&#xff0c;而不需要手动编写大量的样板代码。Spri…

FRPC+PHP+MYSQL+APACHE2=个人网站

应用背景有公网需求,但是又不想去买又贵又低配置的服务器,然后方案就应运而生 frp/README_zh.md at dev fatedier/frp (github.com) 在这里, FRPC作为内网穿透服务, PHPMYSQLAPACHE2,作为网站搭建,具体细节不细讲, 但是在我的/var/www/html下面 linaroHinlink:/var/www/h…

CSS3新增特性(二)

四、2D 转换 • 属性名&#xff1a;transform &#xff08;可用于制作2D转换&#xff0c;也可用于制作3D转转换&#xff1b;2D转换是平面上的转换&#xff0c;3D转换是在三维立体空间的转换&#xff09; • 作用&#xff1a;对元素进行水平或垂直方向的移动、缩放、旋转、拉长…

二. 搭建Nginx 直播流程服务器

目录 1. 前言 2. 安装 Nginx 依赖 3.下载源码 4. 编译安装 5.配置 rtmp 服务 6.验证配置 1. 前言 服务器由 NGINX+RTMP 构成。 NGINX 是 HTTP 服务器, RTMP 是附加模块。 其中 NGINX 我选择的是用 源码编译方式 进行安装,因为这种方式可以自定义安装指定的…

DevOps(八)Jenkins的Maven和Git插件

一、Maven简介 Maven是一个构建生命周期管理和理解工具&#xff0c;用于Java项目。它提供了标准化的构建流程&#xff0c;并简化了从项目编译到文档生成等各种构建方面的管理。 Maven是由Apache软件基金会开发和维护的一个流行的项目管理工具。它的设计目的是简化Java项目的构…

Linux驱动开发:深入理解I2C时序

目录标题 I2C简介I2C时序关键点Linux内核中的I2C时序处理I2C适配器I2C算法I2C核心 代码示例&#xff1a;I2C设备访问调试I2C时序问题 在Linux驱动开发中&#xff0c;理解和正确处理I2C时序对于确保I2C设备正常工作至关重要。本文将详细介绍I2C通信协议的时序特征&#xff0c;并…

应用在防蓝光显示器中的LED防蓝光灯珠

相比抗蓝光眼镜、防蓝光覆膜、软体降低蓝光强度这些“软”净蓝手段&#xff0c;通过对LED的发光磷粉进行LED背光进行技术革新&#xff0c;可实现硬件“净蓝”。其能够将90%以上的有害蓝光转换为450nm以上的长波低能光线&#xff0c;从硬件的角度解决了蓝光危害眼睛的问题&#…

❤️新版Linux零基础快速入门到精通——第一部分❤️

❤️新版Linux零基础快速入门到精通——第一部分❤️ 非科班的我&#xff01;Ta&#xff01;还是来了~~~1. 来认识一下Linux吧!1.1 操作系统概述1.1.1 操作系统概述1.1.2 操作系统的发展史1.1.2.1 Unix1.1.2.2 Minix1.1.2.3 Linux 1.1.3 操作系统的发展 1.2 Linux初识1.2.1 Lin…

【MySQL】数据库操作指南:数据类型篇

&#x1f331;博客主页&#xff1a;青竹雾色间 &#x1f331;系列专栏&#xff1a;MySQL探险日记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ✨人生如寄&#xff0c;多忧何为 ✨ 文章目录 1. 数值类型1.1 tinyint 类型1.2 bit 类型1.3 小数类型1.3.1 f…

nacos配置mysql(windows)

nacos默认是使用的内置数据库derby ,可通过配置修改成mysql,修改成mysql之后&#xff0c;之前配置在derby的数据会丢失 本文使用mysql版本为8.0.22 nacos版本为2.3.1 在mysql里面先创建一个数据库test(名称自定义&#xff0c;和后面配置文件里面的一样就好了) 在上面创建的数据…

Milvus 在哈啰的应用与落地

向量数据库还有哪些可能性&#xff1f; 本期的【User Tech】直播告诉你答案&#xff01;明晚的直播&#xff0c;我们邀请了来自哈啰的资深研发工程师王永辉&#xff0c;他将为我们详细讲解 Milvus 在本地出行及生活服务平台的应用及未来发展的诸多可能性&#xff0c;敬请期待&a…

如何在Windows服务做性能测试(CPU、磁盘、内存)

目录 前言1. 基本知识2. 参数说明 前言 由于需要做一些接口测试&#xff0c;测试是否有真的优化 1. 基本知识 该基本知识主要用来用到Performance Monitor&#xff0c;以下着重介绍下这方面的知识 性能监视器&#xff08;Performance Monitor&#xff09;&#xff1a;Windo…

C++ 核心编程 - 内存分区模型

文章目录 1.1 程序运行前1.2 程序运行后1.3 new 操作符 C 程序在执行时&#xff0c;将内存大致划分为 4个区域&#xff1a; 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理&#xff1b;全局区&#xff1a;存放全局变量和静态变量以及常量&#xff1…

MT2041 三角形的个数

思路&#xff1a;找规律&#xff0c;推公式 4等分&#xff1a; 头朝上的三角形&#xff1a; 边长为1&#xff1a;1234s1&#xff1b; 边长为2&#xff1a;123s2&#xff1b; 边长为3&#xff1a;12s3&#xff1b; 边长为4&#xff1a;1s4&#xff1b; 即si12...n-i1(n-i2)*(n-i…

基于高斯混合模型的视频背景提取和人员跟踪算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2013B 3.部分核心程序 .............................................................................. %我们这里…

根据当年节假日和非工作时间计算请假时间-获取每个月的节假日,计算每个月的工作日时间进度,节假日每年更新

根据需求请假时间要排除法定节假日和非工作时间 1.获取当年的节假日 节假日是每年更新的&#xff0c;没有固定接口&#xff0c;需要手动录入 个人根据官方的节假日整理了当年的所有节假日&#xff0c;可以根据个人需求进行修改 // 获取每个月的节假日&#xff0c;如果当月没…

Java | 冒泡排序算法实现

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 编写一个Java程序&#xff0c;实现冒泡排序算法。程序需要能够接收一个整型数组作为输入&#xff0c;并输出排序后的数组。 冒泡排序是一种简单的排序算法&#xff0c;它…

Laravel 6 - 第十五章 验证器

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

记录好用的python包

记录好用的python包 PipxCentos 安装pipx确保 Pip 被安装更新 Pip安装 Pipx添加 Pipx 到 PATH临时添加到 PATH:永久添加到 PATH: 验证 Pipx 安装 Poetry安装准备工作创建虚拟环境激活虚拟环境安装包追踪 & 更新包常用配置pycharm 远程连接poetry创建的虚拟环境参考链接 cen…

Java基础入门day36

day36 js dom控制css变化 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title>…