Linux应用开发笔记(五)网络编程(二)多线程编程

news2025/2/6 5:55:36

文章目录

  • 前言
  • 一、线程和进程
    • 1. 进程(Process)
    • 2. 线程(Thread)
    • 3. 二者的比较
  • 二、多线程和多进程
  • 三. 代码编写
    • 1. 相关函数
      • pthread_create( )函数
      • pthread_exit( )函数
      • pthread_join( )函数
    • 2. 线程同步
    • 3. 互斥量
    • 4. 条件变量
    • 5. 实验代码


前言

  在前面的学习中,我们提到了ROTS操作系统的特点,即可以多线程操作命令,这样的好处是可以同时操作好几个目标,而不是因为上一个目标未结束使得需要的操作陷入阻塞状态。

一、线程和进程

  这是一个常用的术语,通常情况下线程和进程是指操作系统中用于实现并发执行的两个基本单位,它们各自具有不同的特点和适用场景。

1. 进程(Process)

  定义:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位。它包含了一个程序的当前执行状态,包括程序计数器、内存指针以及多个寄存器的当前值等。
  资源占用:每个进程都拥有独立的内存空间和系统资源,如代码、数据、堆栈等。
  独立性:进程之间是相互独立的,一个进程的崩溃不会影响到其他进程。
  通信与同步:进程间的通信(IPC)通常通过管道、消息队列、共享内存等方式实现,而同步则需要使用信号量、互斥锁等机制。
  开销:由于进程拥有独立的资源,因此创建、销毁和切换进程的开销相对较大。

2. 线程(Thread)

  定义:线程是进程内的一条执行路径或执行流,是系统调度的基本单位。线程共享进程的资源,包括代码、数据、打开的文件、信号处理器和进程ID等。
  资源占用:线程之间共享进程的内存空间和系统资源,但每个线程拥有自己独立的程序计数器、寄存器和堆栈。
  通信与同步:线程间的通信和同步相对简单,可以通过共享内存直接访问,但也需要使用适当的同步机制来避免数据竞争和不一致。
  开销:由于线程共享进程的资源,因此创建、销毁和切换线程的开销相对较小。

3. 二者的比较

  独立性:进程是完全独立的,而线程则依赖于进程。
  资源占用:进程占用独立的资源,而线程共享进程的资源。
  开销:进程的创建、销毁和切换开销较大,而线程的开销较小。
  并发性:由于线程的开销较小,因此多线程可以实现更高的并发性。

二、多线程和多进程

  多进程:进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,也是操作系统结构的基础。当需要运行多个独立的程序或需要完全隔离的资源时,使用多进程是合适的。例如,在服务器上运行多个独立的服务。其特点为:

  • 独立性:每个进程都有自己的独立地址空间,包括代码、数据、栈等,彼此之间相互隔离,不会相互干扰或影响。
  • 并发性:操作系统可以同时运行多个进程,每个进程独占一定的系统资源,通过切换和调度来实现并发执行。
  • 优势:稳定性高(一个进程的崩溃不会影响其他进程的运行)、数据隔离(进程间数据独立,简化了数据同步的问题)。
  • 劣势:资源消耗大 (每个进程都需要独立的内存空间和系统资源,因此资源消耗相对较大)、切换开销大(进程上下文切换的开销相对较大,可能影响系统的整体性能)。

  多线程:多线程是指从软件或硬件上实现多个线程并发执行的技术。具有多线程能力的计算机能够在同一时间执行多于一个线程,进而提升整体处理性能。当需要在一个程序中并发执行多个任务,且这些任务需要共享某些资源时,使用多线程是合适的。例如,GUI程序中的事件处理、网络编程中的并发连接处理等。其特点大体如下:

  • 并发执行:多个线程可以同时执行,不必等待其他线程完成。
  • 共享资源:多个线程可以共享同一份资源,例如内存、文件等。
  • 独立性:每个线程都有自己的执行上下文和栈空间,彼此之间相互独立。
  • 优势:资源消耗少(线程间共享进程资源,所以资源消耗相对较少)、切换快(线程的上下文切换相对较快,有助于提高CPU的利用率)
  • 劣势:数据同步困难(由于线程间共享数据,数据同步可能会变得复杂和困难)。

三. 代码编写

1. 相关函数

pthread_create( )函数

//功能:创建一个子线程
 #include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
	void *(*start_routine) (void *), void *arg);
  • 参数
    - thread:传出参数,线程创建成功后,子线程的线程ID被写到该变量中。
    - attr : 设置线程的属性,一般使用默认值,NULL
    - start_routine : 函数指针,这个函数是子线程需要处理的逻辑代码
    - arg : 给第三个参数使用,传参
  • 返回值
    - 成功:0
    - 失败:返回错误号。这个错误号和之前errno不太一样。
    - 获取错误号的信息: char * strerror(int errnum);

pthread_exit( )函数

//终止一个线程,在哪个线程中调用,就表示终止哪个线程
#include <pthread.h>
void pthread_exit(void *retval);
  • 参数(retval):需要传递一个指针,作为一个返回值,可以在pthread_join( )中获取到。

  需要注意的是,pthread_exit并不等同于普通的C库函数exit。exit会终止整个进程,而pthread_exit只终止调用它的那个线程。其他线程会继续执行,直到它们也各自调用pthread_exit,或者主线程返回,这时整个进程才会结束。

pthread_join( )函数

//阻塞调用它的线程,直到指定的thread线程终止
int pthread_join(pthread_t thread, void **retval);
  • 参数:
    - thread:需要回收的子线程的ID
    - retval: 接收子线程退出时的返回值
  • 返回值: 成功 – 0
      当thread线程调用pthread_exit并返回时,pthread_join会解除阻塞,并可以通过retval参数获取thread线程的退出状态。如果retval是NULL,那么就不会获取线程的退出状态。

2. 线程同步

  线程同步是指在多个线程之间协调共享资源的访问,以保证数据的一致性和正确性。基本的线程同步原理是通过协调线程之间的访问顺序,确保共享资源的正确访问。当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。线程同步可以避免竞态条件、死锁、饥饿等问题。实现线程同步有多种方式,如临界区、互斥量、信号量和事件等。

  • 临界区(Critical Section):通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
  • 互斥量(Mutex):为协调共同对一个共享资源的单独访问而设计,可以指定资源被独占的方式使用。
  • 信号量(Semaphore):为控制一个具有有限数量用户资源而设计,允许多个线程在同一时刻访问同一资源,但需要限制在同一时刻访问此资源的最大线程数目。
  • 事件(Event):用来通知线程有一些事件已发生,从而启动后继任务的开始。

  这些同步方式可以根据具体的应用场景和需求来选择使用,以确保线程之间能够正确地共享和访问资源,提高程序的稳定性和效率,本次实验我们使用互斥量。

3. 互斥量

  互斥量(Mutex,全称互斥锁或互斥对象)是一种用于线程同步的编程工具,它允许一个线程独占某个共享资源,以防止其他线程同时访问。互斥量通常用于保护对共享数据的访问,以避免数据竞争和不一致的问题。通常情况下,我们可以将他理解为一把“锁”,当进入目标线程时,这把锁扣起来,在线程内的任务结束时再把锁打开,其大致流程如下所示。
在这里插入图片描述
  互斥锁和信号量不同的是,它具有互斥锁所有权、递归访问等特性,常用于实现对临界资源的独占式处理, 在任意时刻互斥锁的状态只有两种,开锁或闭锁。当互斥锁被线程持有时,该互斥锁处于闭锁状态,线程获得互斥锁的所有权。当该线程释放互斥锁时, 该互斥锁处于开锁状态,线程失去该互斥锁的所有权。也就是说,同时只有一个线程能获取互斥锁,特别地,持有该互斥锁的线程能够再次获得这个锁而不被阻塞, 这就是互斥锁的递归访问,这个特性与一般的信号量有很大的不同, 在信号量中,由于会不存在可用的信号量,线程递归获取信号量时会发生阻塞,最终形成死锁。
  想要避免死锁,最好遵循以下的规则:

  • 对共享资源操作前一定要获得锁。
  • 完成操作以后一定要释放锁。
  • 尽量短时间地占用锁。
      如果有多个锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。

4. 条件变量

  既然已经掌握了Mutex,我们便再引入它的一个“好伙伴”----条件变量(Condition Variables)。条件量是计算机编程中用于处理并发编程的一种同步机制。在多线程或多进程环境中,条件量通常与互斥锁(mutex)一起使用,以允许线程或进程在满足特定条件之前等待,并在条件满足时被唤醒。它的主要作用是让线程能够在某个条件不成立时进入阻塞状态,等待其他线程改变条件并通知它。一旦条件成立,被阻塞的线程会被唤醒并继续执行。这种机制可以避免线程轮询检查条件是否成立,从而节省CPU资源并提高程序的性能。其大致流程即相关函数如下:
在这里插入图片描述

5. 实验代码

//这是一个多线程案例,其主要目标是通过其中的一个线程进行发送,再经由另一个线程接
收达到回环的目标
#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
//初始化
#include<semaphore.h>
static char re_buff[256];
//创建互斥量并初始化,防止持续写入
static pthread_mutex_t tmutex = PTHREAD_MUTEX_INITIALIZER; 
static pthread_cond_t tcond = PTHREAD_COND_INITIALIZER;

static void *my_thread_func(void *data){
    while(1){
    	//上锁
        pthread_mutex_lock(&tmutex);
        //等待条件
        pthread_cond_wait(&tcond,&tmutex);

        printf("rec: %s\n",re_buff);
        //解锁
        pthread_mutex_unlock(&tmutex);
    }
        return NULL;
}

int main(int argc, char **argv){
    pthread_t tid;
    int res;
    //防止主线程长期霸占互斥量
    char buff[256];
    //创建接收线程(若成功返回0)
    res = pthread_create(&tid,NULL,my_thread_func,NULL);
    //验证是否创建成功
    if(res != 0){
        printf("create error!\n");
        return -1;
    }
    //主线程读取标准输入发给接收线程
    while(1){
        fgets(buff,256,stdin);
        //上锁
        pthread_mutex_lock(&tmutex);
        memcpy(re_buff,buff,256);
        //通知接收线程
        pthread_cond_signal(&tcond);
        //解锁
        pthread_mutex_unlock(&tmutex);
    }
    return 0;
}

在这里插入图片描述
注:在gcc -o 编译时需要加上扩展库(-lpthread),例如:

gcc -o thread.o thread.c -lpthread

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

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

相关文章

微服务之分布式链路追踪

一、概述 1.1背景 在微服务框架中&#xff0c;一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果&#xff0c;每一个前段请求都会形成一条复杂的分布式服务调用链路&#xff0c;链路中的任何一环出现高延时或错误都会引起整个请求最…

MyBatis操作数据库(3)

其它查询操作 #{}和${} MyBatis参数赋值有两种方式, 咱们前面使用了#{}进行赋值, 接下来来看两者的区别: #{}和${}的使用 1.先看Integer类型的参数: Select("select username, password, age, gender, phone from userinfo where id #{id}") UserInfo queryByI…

OSI七层网络攻击行为及防范手段

2020年3月3日&#xff0c;360安全大脑披露美国中央情报局攻击组织&#xff08;APT-C-39&#xff09;对我国大型互联网公司、政府部门及相关企业进行长达11年的网络攻击渗透&#xff0c;该组织所使用的网络武器和CIA“Vault7”项目中的网络武器完全吻合。如今随着互联网技术的蓬…

RocketMQ 事件驱动:云时代的事件驱动有啥不同?

作者&#xff1a;林清山&#xff08;隆基&#xff09; 前言&#xff1a; 从初代开源消息队列崛起&#xff0c;到 PC 互联网、移动互联网爆发式发展&#xff0c;再到如今 IoT、云计算、云原生引领了新的技术趋势&#xff0c;消息中间件的发展已经走过了 30 多个年头。 目前&a…

图片懒加载的三种方式

方法一:滚动监听 + scrollTop + offsetTop + innerHeight scrollTop:指网页元素被滚动条卷去的部分。 offsetTop:元素相对父元素的位置 innerHeight:当前浏览器窗口的大小。需要注意兼容性问题。 <!DOCTYPE html> <html lang="en"><head>&…

IDEA: Unable to resolve table ‘xxx‘

描述&#xff1a; 在 IDEA 连接到数据库后&#xff0c;SQL 语句提示 Unable to resolve table 表名&#xff0c;且其它字段也飘红报错。 解决&#xff1a; 右键点击数据库&#xff0c;选择 Tools -> Manage Shown Schemas... 勾选你所使用的数据库即可&#xff1a; 1、2、3…

软考 系统架构设计师系列知识点之大数据设计理论与实践(6)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之大数据设计理论与实践&#xff08;5&#xff09; 所属章节&#xff1a; 第19章. 大数据架构设计理论与实践 第3节 Lambda架构 本文部分内容参考&#xff1a; Lambda架构 - 简书 特此致谢&#xff01; 19.3.3 Lambda…

【Ansible自动化运维】Ansible入门基础信息【安装配置、常用命令与模块】

介绍安装配置注意事项yum安装验证安装配置host配置主机清单配置主控端被控端 常用模块命令组成command模块shell模块copy模块script模块 日志信息最后 介绍 Ansible 是一个开源 IT 自动化引擎&#xff0c;可自动执行供应、配置管理、应用程序部署、编排和许多其他 IT 流程。它可…

深入浅出学习切片LOD——ArcGIS server模拟缓存切片(影像快显)

一、第一次实践 原理 免切片实现影像服务的模拟切片&#xff0c;主要原理是接收前端传过来的xyz(行列层级)以及切片方案&#xff0c;计算出该请求的切片的四至经纬度信息&#xff0c;通过mapserver的exportImage接口&#xff0c;传入每个模拟切片的四至经纬度信息得到图片返回…

小程序视频下载器

下载高手&#xff0c;让小程序视频下载变得前所未有的简单&#xff01;专为非编程专业人士设计&#xff0c;该工具免去了繁琐的抓包软件学习过程&#xff0c;无需深入研究Fiddler或Charles的配置。它优化了视频、图片和音频资源的下载&#xff0c;提供直观的操作界面&#xff0…

大世界基尼斯见证辉煌,云仓酒庄首届酒类培训新高度诞生

近日&#xff0c;一场规模盛大的酒类培训盛会&#xff0c;在云仓酒庄的精心组织下圆满落幕。此次培训活动以其卓着的成果和盛大的规模&#xff0c;创下了大世界基尼斯纪录&#xff0c;为酒类培训领域树立了新的标杆。这一成就的取得&#xff0c;背后是云仓酒庄团队无数的心血与…

修改taro-ui-vue3的tabs组件源码增加数字标签

需求&#xff1a;taro-ui-vue3的tabs组件上增加数字标记 步骤一&#xff1a;node_modules文件夹下找到taro-ui-vue3/lib/tabs/index.js 把173行的这一段替换成下面这段&#xff0c;然后写上样式 default: () > item.number ? [h(View, {class: at-tabs__item_in}, {defau…

linux应急响应基础命令

一、cpu使用率-top top -c -o %CPU -c 显示进程的命令行参数 -o 按照CPU占用从大到小排序二、用户信息 1、查看系统所有用户信息 [rootcentos7 ~]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nol…

有道词典网页版接口分析与爬虫研究

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、目标站点 有道词典网页版&#xff1a;网易有道 二、目标接口 url&#xff1a;https://dict.youdao.com/jsonapi_s?doctypejson&…

JUC专题——Java并发机制的底层实现原理

本文部分内容节选自《Java并发编程的艺术》 volatile 的应用 volatile 是轻量级的 synchronized, 它在多处理器开发中保证了共享变量的 “可见性”. 可见性的意思是当一个线程修改一个共享变量时, 另外一个线程能读到这个修改的值. 如果 volatile变量修饰符使用恰当的话, 它比…

面试算法-173-二叉树的直径

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,…

linux的线程概念

目录 1.原理 2.线程的周边概念 3.创建线程的接口 1.pthread_create 2.pthread_join 3.pthread_detach 4.终止线程 5.C11封装的多线程库 4.线程库的大概结构 5.__thread&#xff08;只能修饰内置类型&#xff09; 6.线程的互斥 1.了解原理 2.加锁 1.接口 2.代码示…

护网 | 如何从蓝队初级进化到蓝队中级

了解应急响应的流程 1&#xff09;首先判断服务器资产、影响范围以及严重程度&#xff0c;确认有没有必要将服务器下线隔离&#xff0c;然后根据服务器的失陷时间和态势感知的告警&#xff0c;判断是由什么漏洞进来的 2&#xff09;其次就是取证排查阶段&#xff0c;如果是w…

get_program_dir() Ai回答是一个函数,用于获取当前程序.exe的目录。

#include <iostream> #include <filesystem>std::string get_program_dir() {return std::filesystem::current_path().string(); }int main() {// 调用函数获取当前程序的目录std::string program_dir get_program_dir();std::cout << "当前程序的目录…

认识一下RAG

1.RAG技术背景与挑战 2.RAG的核心概念 3.RAG的工作流程与架构 4.RAG的优化方法 RAG的提出 •Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks是一篇重要的论文(2020年5月) •REALM: Retrieval-Augmented Language Model Pre-Training (2020)就将BERT预训练模…