【项目 线程 1】 3.1线程概述 3.2创建线程 3.3终止线程 3.4连接已终止的线程

news2024/11/17 13:38:16

文章目录

    • 3.1线程概述
      • 线程概述
      • 线程和进程区别
      • 线程和进程虚拟地址空间
      • 线程之间共享和非共享资源
      • NPTL
    • 3.2 创建线程
      • 线程操作
      • 创建线程
      • 出现报错及原因
    • 3.3终止线程
    • 3.4连接已终止的线程


3.1线程概述

线程概述

在这里插入图片描述并发:两队人用同一个咖啡机(本质上同一时刻只有一个进程在运行,但是切换非常快,所以看起来是好几个进程同时进行)
并行:两队人用两个咖啡机

查看指定进程的线程:ps -Lf pid(进程号)
在这里插入图片描述

线程和进程区别

在这里插入图片描述

线程和进程虚拟地址空间

进程是读时共享,写时复制。但不论怎么说,只要创建了一个新进程,内核一定先被复制。写的时候还要创建新的实际物理空间。

线程共享虚拟地址空间,只是**.text段(代码段)以及栈空间**有不一样。.text会分成一个个小段,线程1、线程2…分开存储在不同的段.text。各线程执行各自的代码。栈空间也会有不同,分成一个个小段分给各线程。
在这里插入图片描述

线程之间共享和非共享资源

在这里插入图片描述信号掩码:每个线程的信号阻塞不一样

NPTL

Linux系统线程库的发展概述
在这里插入图片描述查看当前线程库版本:
在这里插入图片描述

3.2 创建线程

线程操作

在这里插入图片描述

创建线程

在这里插入图片描述

出现报错及原因

在这里插入图片描述

线程是第三方库不是标准系统库,需要通过-l指定去指定库的名称
解决方案如下:加上第三方库 -pthread 或者 -lpthread

wcx@wcx-virtual-machine:~/linux/lesson29$ gcc pthread_create.c -o pthread_create -pthread

在这里插入图片描述

main函数中所有执行的代码为主线程的代码,子线程执行的代码为回调函数的代码

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

//函数指针,void*为万能指针
void * callback(void * arg) {
    printf("child thread...\n");
    printf("arg value: %d\n", *(int *)arg);
    return NULL;
}

int main() {

    pthread_t tid;

    int num = 10;

    // 创建一个子线程
    int ret = pthread_create(&tid, NULL, callback, (void *)&num);

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    } 

    for(int i = 0; i < 5; i++) {
        printf("%d\n", i);
    }

    //预防子线程还在创建或子线程未抢占执行权,程序就退出,预防子线程的未被执行
    sleep(1);

    return 0;   // exit(0);
}

显示结果:
在这里插入图片描述

3.3终止线程

让主线程退出,当主线程退出时,不会影响其他正常运行的线程(子线程)。因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程

pthread_exit(NULL); //因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程

	//该句没有被执行
    printf("main thread exit\n");

	//没有执行
    return 0;   // exit(0);

在这里插入图片描述

#include <stdio.h>
#include <pthread.h>
#include <string.h>

void * callback(void * arg) {
    printf("child thread id : %ld\n", pthread_self());
    return NULL;    //相当于 pthread_exit(NULL);终止子线程
} 

int main() {

    // 创建一个子线程
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, callback, NULL);

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    }

    // 主线程
    for(int i = 0; i < 5; i++) {
        printf("%d\n", i);
    }

    printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());

    // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程(子线程)。
    pthread_exit(NULL); //因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程

	//该句没有被执行
    printf("main thread exit\n");

	//没有执行
    return 0;   // exit(0);
}

在这里插入图片描述
下图说明主线程和子线程是交替执行的
在这里插入图片描述

3.4连接已终止的线程

在这里插入图片描述子进程一定是被父进程回收,子线程不一定需要被父线程回收,可以被任意线程回收,但一般是主线程回收子线程。如果子线程结束后不被回收,也会产生僵尸线程。

为何使用二级指针?
要想改变一级指针的值,函数调用传入参数为二级指针。
因为返回值是一个指针,想通过retval传出返回值,肯定要用指针。

int a = 0
void act(&a)(){}

肯定是这样才能改变a的值。
同理如果a是一个指针类型,形参理所当然就是二级指针

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

//全局变量,若为局部变量,栈空间数据,最后获取的是个随机的值
//线程退出时记得一定返回的是全局变量的值
int value = 10;

void * callback(void * arg) {
    printf("child thread id : %ld\n", pthread_self());
    // sleep(3);
    // return NULL; 
    // int value = 10; // 局部变量
    pthread_exit((void *)&value);   // return (void *)&value;
} 

int main() {

    // 创建一个子线程
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, callback, NULL);

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    }

    // 主线程
    for(int i = 0; i < 5; i++) {
        printf("%d\n", i);
    }

    printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());

    // 主线程调用pthread_join()回收子线程的资源
    int * thread_retval;//定义一个一级指针
    //传递一级指针的地址作为二级指针,并转换为void **
    ret = pthread_join(tid, (void **)&thread_retval);
    

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    }

    printf("exit data : %d\n", *thread_retval);

    printf("回收子线程资源成功!\n");

    // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程。
    pthread_exit(NULL);

    return 0; 
}

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

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

相关文章

算法竞赛入门【码蹄集新手村600题】(MT1100-1120)C语言

算法竞赛入门【码蹄集新手村600题】(MT1100-1120&#xff09;C语言 目录MT1101 带参数的宏IIMT1102 长方体MT1103 球体积MT1104 三角形MT1105 英寸英尺英里MT1106 盎司磅MT1107 加仑/升MT1108 保留小数MT1109 和10相比MT1110 最小值MT1111 最大值MT1112 中庸之道MT1113 三人同行…

这可是全网最全的网络工程师零基础实战视频整理,最新版分享

互联网中每一项傍身的技能都是需要从如何入门开始的&#xff0c;网络技术也是如此&#xff01; 网络技术区别其他互联网技能的一点是学习需要从设备开始&#xff0c;只有认识了解了路由器、交换机、防火墙这些网络设备&#xff0c;才开始从网络通信原理开始&#xff0c;这使得网…

数据安全与可追溯:PDM系统的信息保护锦囊

在当今数字化时代&#xff0c;数据安全与可追溯是企业管理中至关重要的环节。PDM系统&#xff08;Product Data Management&#xff0c;产品数据管理&#xff09;作为一款强大的数字化工具&#xff0c;为企业提供了全方位的信息保护锦囊。让我们一同深入探讨&#xff0c;看看PD…

k8s手动发布镜像的方法

kubectl edit deploy编辑对应的文件&#xff0c;并:wq!保存即可

STL C++学习背景

STL C学习背景 背景知识 背景知识 STL前置知识 STL&#xff0c;英文全称 standard template library&#xff0c;中文可译为标准模板库或者泛型库&#xff0c;其包含有大量的模板类和模板函数&#xff0c;是 C 提供的一个基础模板的集合&#xff0c;用于完成诸如输入/输出、数…

2023新款MacBook Pro 14效果如何

新款MacBook Pro 14 显示屏背面依然保持着苹果LOGO&#xff0c;而且比Air大一圈&#xff0c;看来贵是有道理的&#xff0c;LOGO都变大了&#xff01;该机配件是一个67W的充电头&#xff0c;以及MagSafe 3的充电线。而机身金属感十足&#xff0c;上手体验&#xff0c;确实有万元…

Java阶段五Day21

Java阶段五Day21 文章目录 Java阶段五Day21问题解析rocketmq清空数据 linux学习背景什么是linux系统虚拟机介绍启动 虚拟机linux虚拟机网络的问题 linux系统的基础命令命令提示符命令格式pwd指令ls指令cd指令mkdirtouch指令cp指令rm指令mv指令cat指令tail指令 文本编辑器vim操作…

代码随想录算法训练营第八天| 28.找到字符串中第一个匹配项的下标,459.重复的子字符串(二刷三刷)

28. 找出字符串中第一个匹配项的下标 28.找到字符串中第一个匹配项的下标 KMP算法 原串&#xff1a;sadbutsad 匹配串&#xff1a;sad 构造next数组其实就是计算模式串s的前缀表的过程。与原串是无关的 关于最长公共前缀和最长公共后缀&#xff1a; 前缀是指不包含最后一…

cmake配置Qt工程

cmake 工程配置 # 指定版本和项目 cmake_minimum_required(VERSION 3.10) set(TARGET_NAME labelDeviceView) project(${TARGET_NAME} ) include(${CMAKE_CURRENT_LIST_DIR}/../../../../../../ossLib/ossLib/env.cmake) set(CMAKE_PREFIX_PATH "D:/Qt6/6.5.2/msvc2019…

进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区

fork函数讲解 进程复制fork基本使用简单分页 逻辑页 物理页 页表fork的三个面试练习题缓冲区僵死进程孤儿进程写时拷贝 进程替换exexc 介绍简易命令解释器strtok()函数讲解 进程复制 fork基本使用 父进程fork后&#xff0c;新的进程产生&#xff0c;新的进程就继续从fork往后的…

Spring集成Web

目录 1、简介 2、监听器 3、Spring提供的listener 3.1、xml 3.2、配置类 3.3、WebApplicationContextUtils 3.4、说明 4、自己复现的listener 4.1、ContextLoaderListener 4.2、WebApplicationContextUtils 4.3、Web调用 ⭐作者介绍&#xff1a;大二本科网络工程专业…

Less is More: Focus Attention for Efficient DETR

摘要 类似detr的模型显著提高了探测器的性能&#xff0c;甚至优于经典的卷积模型。然而&#xff0c;在传统的编码器结构中&#xff0c;所有的标记都带来了冗余的计算负担。最近的稀疏化策略利用了信息标记的一个子集&#xff0c;通过稀疏编码器来降低注意力的复杂性&#xff0…

观测,让运维更简单!龙蜥社区系统运维 MeetUp 等你报名

为了让广大社区用户和开发者近距离感受 Linux 和 eBPF 技术的魅力&#xff0c;龙蜥社区系统运维于 08 月 12 日在杭州举行 MeetUp 。过去&#xff0c;系统运维 SIG 和大家一起交流了 eBPF 在安全和网络的最佳应用和实践&#xff0c;以及 Linux 在手机和服务器上的运维经验等技术…

深度学习训练营之CGAN生成手势图像

深度学习训练营之CGAN生成手势 原文链接CGAN简单介绍环境介绍前置工作数据导入所需的包加载数据创建数据集查看数据集 模型设置初始化模型的权重定义生成器构造判别器 模型训练定义损失函数设置超参数正式开始训练 结果可视化 原文链接 &#x1f368; 本文为&#x1f517;365天…

leetcode 763. 划分字母区间

2023.8.3 本题的关键是要确保同一字母需要在同一片段中&#xff0c;而这就需要关注到每个字母最后一次出现的位置。 思路&#xff1a;用一个哈希表保存每个字母&#xff08;26个&#xff09;最后一次出现的位置。然后从头遍历&#xff0c;不断更新最右边界&#xff0c;直到当前…

LLVM笔记1

参考&#xff1a;https://www.bilibili.com/video/BV1D84y1y73v/?share_sourcecopy_web&vd_sourcefc187607fc6ec6bbd2c74a3d0d7484cf 文章目录 零、入门名词解释1. Compiler & Interpreter2. AOT静态编译和JIT动态解释的编译方式3. Pass4. Intermediate Representatio…

Eureka增加账号密码认证登录

一、业务背景 注册中心Eureka在微服务开发中经常使用到&#xff0c;用来管理发布的微服务&#xff0c;供前端或者外部调用。但是如果放到生产环境&#xff0c;我们直接通过URL访问的话&#xff0c;这显然是不安全的。 所以需要给注册中心加上登录认证。 通过账号和密码认证进行…

openGauss学习笔记-30 openGauss 高级数据管理-别名

文章目录 openGauss学习笔记-30 openGauss 高级数据管理-别名30.1 语法格式30.1.1 列别名语法30.1.2 表别名语法 30.2 参数说明30.3 示例 openGauss学习笔记-30 openGauss 高级数据管理-别名 SQL可以重命名一张表或者一个字段的名称&#xff0c;这个名称为该表或该字段的别名。…

Spring:JDBCTemplate

JDBCTemplate 概述 概述 JDBC&#xff08;Java DataBase Connectivity&#xff0c;Java 数据库连接&#xff09;&#xff0c; 一 种用于执行 SQL 语句的 Java API&#xff08;Application Programming Interface &#xff0c; 应用程序设计接口 &#xff09;&#xff0c;可以为…

【每日一题】—— C. Mocha and Hiking(Codeforces Round 738 (Div. 2))

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…