Linux下多线程的操作

article/2025/10/31 5:02:33

线程定义

**定义:**线程是轻量级的进程(LWP:light weight pocess),在Linux环境下线程的本质仍然是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源,可以这样理解,进程是资源分配的最小单位,线程是操作系统调度执行的最小单位。

线程和进程之间的区别:
• 进程有自己独立的地址空间,多个线程公用同一个地址空间

  1. 线程更加节省系统空间,效率不仅可以保持的,而且能够更加高
  2. 在一个地址空间中多个线程独享:每个线程都有属于自己的栈区,寄存器(内核中管理的)
  3. 在一个地址空间中多个线程共享:代码区,堆区,全局数据区,打开的文件(文件描述符表)都是线程共享的

• 线程是程序是最小执行单位,进程是操作系统系统中最小的资源分配单位

  1. 每个进程对应一个虚拟地址空间,一个进程只能抢一个CPU时间片
  2. 一个地址空间中可以划分出多个线程,在有效的资源基础上,能够抢更多的CPU时间片

每个线程去抢CUP片都是随机的,是概率问题。下图是理想状态
在这里插入图片描述

  1. CPU的调度和切换:线程的上下文切换比进程要快的多
    上下文切换:进程/线程分时复用CPU时间片,在切换之前会将上一个任务得到状态进程保存,下次切换会这个任务时候,加载这个状态继续运行,任务从保存到再次加载这个过程就是一次上下文切换。状态保存在寄存器中。

  2. 线程更加廉价,启动速度更快,退出也快,对系统资源的冲击小。

在处理多任务程序的时候使用多线程比使用多进程要更加有优势,但是线程并不是越多越好,如何控制线程的个数呢?

  1. 文件IO操作:文件IO对CPU是使用率不高,因此可以分时复用CPU时间片,线程的个数=2*CPU核心数(效率最高)
  2. 处理复杂算法(主要是CPU进行运算,压力大),线程的个数=CPU的核心数(效率最高)
    在这里插入图片描述

创建线程

线程函数
每个线程都有一个唯一的线程ID,ID类型为:pthread_t,这个ID类型为一个无符号长整型数,如果想要得到当前线程的ID,可以调用如下函数:
头文件:#include<pthread.h>
函数原型:pthread_t pthread_self(void);//返回当前线程ID
下面是在linux下测试的数据图。
在这里插入图片描述

pthread_create
在一个进程中调用线程创建函数,就可以达到一个线程,和进程不同,需要给每个创建出的线程指定一个处理的函数,否则这个线程无法工作。
头文件:#include<pthread.h>
函数原型:int pthread_create(pthead_t *thread, const pthead_attr_t *attr, void *(*start_routine)(void *), void * arg);
线程库得到名字叫lpthread,全名:libpthread.so libpthread.a
参数:

  1. thead:传出参数,是无符号长整型,线程创建成功,会将线程ID写入到这个指针指向的内存中。
  2. attr:线程的属性,一般情况下使用默认属性即可,写NULL
  3. start_routine:函数指针,创建出的子线程的处理动作,也就是该函数在子线程中执行。
  4. arg:作为实参传递到start_routine指针指向的函数内部。(如果参数多可以定义一个结构体然后传入一个结构体就好了)
    **返回值:**线程创建成功返回0,创建失败返回对应的错误号。

由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数 test_5_6_2
在这里插入图片描述
下面是源码:
在这里插入图片描述

为什么没有输出子进程数据?
原因: 因为程序是从main函数开始执行,从主线程进来之后,然后创建出来子线程,有可能子线程还没有抢到时间片,主线程就执行完毕了,就会把地址空间释放了,子线程的家就没有了。因此子线程所有的资源也就被释放了。
解决方案: 我们可以让主线程等一会,sleep(2),表示主线程睡眠3秒,睡眠就会放弃CPU资源,不消耗系统资源

当我们加入sleep(3)时执行如下:
在这里插入图片描述

线程退出

在编写多线程程序的时候,如果想要让线程退出,但是不会导致虚拟地址空间的释放(针对于主线程),我们就可以调用线程库退出函数,只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运算,不管是在子线程或者主线程都可以使用。
头文件: #include<pthread.h>
函数原型: void pthread_exit(void *retval);
参数: 线程退出的时候携带的数据,当前子线程的主线程会得到该数据,(需要配合线程回收使用)如果不需要使用,就指定为NULL
测试结果:
在这里插入图片描述
源代码:
在这里插入图片描述

线程回收

线程函数
线程和进程一样,子线程退出的时候其内核资源主要由主线程回收,线程库中提供的线程回收函数叫pthread_join(),这个函数是一个阻塞函数,如果还有子线程在运行,调用该函数就会阻塞,子线程退出函数解除阻塞进行资源回收,函数被调用一次,只能回收一个线程,如果有多个子线程则需要循环回收。释放的实际是内核区的资源。

通过线程回收函数还可以获得到子线程退出时传递出来的数据
头文件: #include<pthread.h>
函数原型:int pthread_join(pthread_t thread, void **retval);
说明 :子线程在运行这个函数就会阻塞,子线程退出,函数解除阻塞,回收对应子线程的资源,类似于回收进程使用的函数wait()
参数

  1. thread:要被回收的子线程的线程ID
  2. retavl:二级指针,指向一级指针的地址,是一个传出参数,这个地址中存储了pthread_exit()传递出来的数据,如果不需要这个参数,可以指定为NULL

返回值: 线程回收成功返回0,回收失败返回错误号。

返回时候切记不能返回堆区变量,所以我们这时候把变量定义为全局变量或者静态变量延长声明周期。还可以把变量创建到主线程的栈空间。
使用子线程栈且定义为static变量
在这里插入图片描述
使用主线程栈
下图为把变量创建到主线程的栈空间。
在这里插入图片描述
create函数创建时候将t的地址传入callback函数,然后callback函数将传入的类型进行强制转换然后赋值,然后执行线程退出函数,主函数使用join回收函数接受。

线程分离函数

在某些情况下,程序中的主线程有属于自己的业务处理流程,如果让主线程负责子线程的资源回收,调用pthread_join()只要子线程不退出主线程就会一直阻塞,主线程的任务也就不能被执行了。

在线程库函数中为我们提供了线程分离函数pthead_detach(),调用这个函数之后指定的子线程可以与主线程分离,当子线程退出的时候,其占用的内核资源就被系统的其他进程接管并回收了。线程分离之后的主线程中使用pthread_join()就回收不到子线程的资源。
测试代码:
在这里插入图片描述
测试结果:
在这里插入图片描述

其他线程函数

线程取消
线程取消就是在某些特定的情况下在一个线程杀死另一个线程。使用这个函数杀死一个线程需要俩步:

  1. 在线程A中调用线程取消函数pthead_cancel(),指定杀死线程B,这时线程B是死不了的。
  2. 在线程B中进行一次系统调用(从用户区切换到内核区),否则线程B可以一直运行。(类似于我吃了毒药,但是只要我不动就没事)

头文件:#include<pthead.h>
函数原型:int pthread_cencel(pthead_t thread);
参数:要被杀死的线程
返回值:函数调用成功返回0,调用失败返回错误码

系统调用

  1. 直接调用Linux系统库函数
  2. 调用标准的C库函数,为了实现某些功能,在Linux平台下标准C库函数会调用相关的系统函数。

下图中红线框起来的区域不会被执行,因为线程已经被杀死了,调用printf函数就相当于执行系统调用,然后就死掉了。
源代码
在这里插入图片描述
测试代码
在这里插入图片描述
下图是把杀死进程注释掉后的结果 我们可以发现下面的图就把上面代码中红色区域的输出来了。
在这里插入图片描述

线程ID比较

在Linux中线程ID本质就是一个无符号长整型,因此可以直接使用比较操作符比较俩个线程的ID,但是线程库是可以跨平台使用的。在某些平台上pthread_t可能不是一个单纯的整型,这种情况下比较俩个线程的ID必须使用函数,

函数原型如下:
头文件:#include<pthread.h>
函数原型:int pthread_equal(pthread_t t1, pthread_t t2);
参数:t1和t2就是要比较的线程ID
返回值:如果俩个线程ID相等返回非0值,如果不相等返回0;


http://chatgpt.dhexx.cn/article/niHXqgko.shtml

相关文章

Linux多线程——概念

目录 前言 一.线程的概念 1.1 什么是线程 1.2 线程的优点 1.3 线程缺点 1.4 线程异常 1.5 线程用途 1.6 Linux进程和线程对比 1.7 关于进程和线程的问题 1.7.1 POSIX线程库 1.7.2 进程ID和线程ID 1.7.3 线程ID和进程地址空间 1.7.4 线程库与内核线程的关系 二.线程管理 …

Linux多线程 | 线程同步

文章目录 前言主要介绍四种常用的线程同步方式以及相关的函数接口。 一、线程同步二、同步方法1.互斥锁2.信号量3.条件变量4.读写锁 总结 前言 主要介绍四种常用的线程同步方式以及相关的函数接口。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一…

linux多线程实验实验报告,Linux多线程实验.ppt

Linux多线程实验.ppt (10页) 本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01; 9.9 积分 Linux多线程实验实验目的掌握Linux多线程程序编写。 Date1实验设备硬件PC机(带网卡) 1台MagicARM270教学实验开…

linux线程全解

目录 一、再论进程1、进程的挂起、阻塞和睡眠的区别&#xff1a;2、多进程实现同时读取键盘和鼠标 二、线程的引入1、线程进程的区别体现在几个方面2、进程与线程的选择取决以下几点3、使用线程技术同时读取键盘和鼠标 三、线程常见函数1、线程创建与回收2、线程取消3、线程函数…

Linux线程详解(概念、原理、实现方法、优缺点)

文章目录 一、Linux线程基本概念二、Linux内核线程实现原理三、创建线程四、线程的优缺点 一、Linux线程基本概念 linux中&#xff0c;线程又叫做轻量级进程&#xff08;light-weight process LWP&#xff09;&#xff0c;也有PCB&#xff0c;创建线程使用的底层函数和进程底层…

Linux 线程———详解

1、线程的概念 和 基础知识 1.1 什么是线程 线程可看作轻量级进程&#xff08;light weight process&#xff09;&#xff0c;Linux的线程本质仍然是进程。Linux先有进程后有线程&#xff0c;当创建了一个进程时&#xff0c;系统给他分配一段4G的虚拟内存&#xff0c;并在其内…

【Linux】线程

前言 目录 1.Linux下的线程概念 2.Linux线程控制&#xff1a;pthread线程库 在单执行流的进程中&#xff0c;此执行流独占了进程的所有资源 在一个进程内部&#xff0c;有时不一定只有一个执行流&#xff0c;在多执行流下&#xff0c;多个执行流共享了进程的地址空间&#xf…

C语言string库strcpy、strcmp、strcat函数详解

strcpy 即string copy 语法格式为strcpy(str1, str2), 作用是将str2赋值给str1 使用方法类似于 char str1[10], str2[] "abc"; strcpy(str1, "bcd"); strcpy(str1, str2); printf("%s", str1); // abcstr2可以是字符串&#xff0c; 也可以是字…

C语言strcpy、strncpy函数是否会复制‘\0‘结束符

1.首先看看strcpy函数的原型 /* strcpy函数原型*/ char *strcpy&#xff08;char *est&#xff0c;const char *src&#xff09; {assert((dest!NULL)&&(src!NULL));char *addressdest;while((*dest *src)!\0);//直到src字符串结束符\0return address; }从while循环条…

C语言中的复制函数(strcpy和memcpy)

strcpy和strncpy函数 这个不陌生&#xff0c;大一学C语言讲过&#xff0c;其一般形式为strcpy&#xff08;字符数组1&#xff0c;字符串2&#xff09;作用是将字符串2复制到字符数组1中去。 EX&#xff1a; char str1[10],str2[]{"China"}; strcpy(str1,str2); st…

C语言进阶——字符串函数2:strcpy函数

strcpy函数的理解 strcpy函数其实是一种替换函数&#xff0c;用arr2中的元素去替换arr1中的元素&#xff0c;如果不够的话就会补上’\0’ strcpy函数的数据类型 char* strcpy(char* destination,const char* source);这个函数的两个参数分别是拷贝的终点和拷贝的源头 strcp…

c语言 strcpy作用,c语言中的strcpy什么意思,简单点解释

strcpy是一个C语言的标准库函数&#xff0c;是string copy(字符串复制)的缩写。strcpy函数的作用是把含有\0结束符的字符串复制到另一个地址空间&#xff0c;返回值的类型为char*。 扩展资料&#xff1a; C语言中&#xff0c;strcpy 函数不对数组边界进行检查&#xff0c;因而在…

strcpy函数的作用是什么

strcpy函数的作用是复制字符串。C 库函数 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。需要注意的是如果目标数组 dest 不够大&#xff0c;而源字符串的长度又太长&#xff0c;可能会造成缓冲溢出的情况。声明下面是 strcpy() 函数的声明。c…

C语言strcpy()函数,字符数组复制

需包含头文件&#xff1a;C 标准库 - <string.h> 文章目录 描述声明参数返回值实例实例 1实例 2 20220511 描述 C 库函数 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。 需要注意的是如果目标数组 dest 不够大&#xff0c;而源字符串…

C++ strcpy_s和strncpy_s使用方法

strcpy_s 函数说明 1、头文件 #include <string.h>2、函数声明 errno_t __cdecl strcpy_s(_Out_writes_z_(_SizeInBytes) char* _Destination,_In_ rsize_t _SizeInBytes,_In_z_ char const* _Source);3、函…

C语言strcpy()函数

前言&#xff1a; strcpy函数的作用是把含有转义字符\0即空字符作为结束符&#xff0c;然后把src该字符串复制到dest&#xff0c;且返回值的类型为“char*”&#xff1b;strcpy是“string copy”&#xff08;字符串复制&#xff09;的缩写。 char *strcpy(char *dest, const ch…

C语言strcpy函数的使用

点击蓝字 关注我们 strcpy简单使用&#xff1a; #include <stdio.h> #include <string.h>struct Student {int sid;char name[20];int age;} st; //此处分号不可省略int main(void) {struct Student st {1000,"zhangsan",20};printf("%d %s %d…

strcpy函数详解看这一篇就够了-C语言(函数实现、使用用法举例、与strncpy的区别)

首先要明确strcpy的功能是&#xff1a;将参数src字符串拷贝至参数dest所指的地址 目录 函数介绍 与strncpy的区别 函数实现1&#xff1a; 函数实现2 改进&#xff1a; 用法示例&#xff1a; 函数介绍 函数原型&#xff1a; char*strcpy&#xff08;char*dest&#xff0c;c…

【C语言】strcpy()函数

文章目录 一、strcpy()函数的简介 二、strcpy()函数的具体使用 三、使用strcpy()函数的注意事项 一、strcpy()函数的简介 strcpy()函数&#xff1a;是将一个字符串复制到另一块空间地址中 的函数&#xff0c;‘\0’是停止拷贝的终止条件&#xff0c;同时也会将 \0 也复制到目…

Sqlserver 查询数据过滤重复,只获取最新一条数据

最近在项目中有这么一个需求&#xff0c;需要从用户提交多条意见中筛选出最新一条数据。因此打算将该语句记录一下。 表结构如下&#xff1a; 普通查询语句&#xff1a; SELECT * FROM T_Dat_OpinionCollection 结果如下&#xff1a; 查询sql&#xff1a; SELECT * FROM (sel…