Linux-多线程2 ——线程等待、线程异常、线程退出、线程取消和线程分离

news2024/11/16 20:26:55

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、线程间的全局变量共享
    • __thread 修饰全局变量
  • 二、线程等待
    • pthread_self和tid
  • 三、线程异常
  • 四、线程退出
  • 五、线程取消
  • 六、线程分离


一、线程间的全局变量共享

上节课我们讲到,线程是共享一个地址空间的,所以对于全局变量,多个线程访问的一定是同一个全局变量。

这里提出一个疑问,既然线程是共享一个地址空间,那么为什么多线程之间为什么不能访问别人的的局部变量呢,答案是没有它们的地址。 但是如果我们通过一个定义一个全局变量来保存别人的局部变量的地址,其实也是可以访问到别的线程的局部变量,不过我们不推荐这么做。

示例代码如下

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>

int count = 0;

void *threadRun(void *args)
{
    while (1)
    {
        count++;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");
    while (1)
    {
        std::cout << "count : " << count << std::endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

__thread 修饰全局变量

如果我们想让每一个线程都私有一份各自的全局变量,各个线程不互相干扰,那么我们就可以使用__thread来修饰。

在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>

__thread int count = 0;

void *threadRun(void *args)
{
    while (1)
    {
        count++;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");
    while (1)
    {
        std::cout << "count : " << count << std::endl;
        sleep(1);
    }
    return 0;
}

二、线程等待

与进程等待类似,当主线程还在运行时,其他新线程已经完成了它们的工作,我们的主线程也要等待我们的新线程来进行资源回收,避免类似于进程僵尸的情况,造成内存泄露问题。

man 3 pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.

参数 pthread_t thread 是线程id,是该你需要等待的线程。
参数 void** retval 是一个输出型参数,用于接收新线程退出时的返回值。
返回值:成功返回0;失败返回错误码
在这里插入图片描述

示例代码如下

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>

void *threadRun(void *args)
{
	//pthread_self()用于返回自己的tid
    printf("%s : %p\n", (char *)args, pthread_self());
    // 为更好观察,休息5s
    sleep(5);
    return (void *)10;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");
	sleep(5);
    int ret;
    pthread_join(tid, (void**)&ret);
    std::cout << "main thread: " << ret << std::endl;
    sleep(5);
    return 0;
}

pthread_self和tid

在上面的示例代码中,我们使用了pthread_slef函数用于返回该线程自身的线程id,可是为什么运行结果输出的并不是我们使用ps -aL命令所显示的LWP呢?

这是因为,我们所使用的pthread属于第三方库,在linux中又叫做原生线程库,对于多线程,我们也需要先描述再组织,而描述者应该是pthread库,所以pthread库中才含有我们的多线程的数据结构,而我们又学习过进程地址空间的分布。

我们的动态库的地址是被加载到共享区的,而pthread_slef所返回的tid其实是被映射的struct pthread的起始地址。
在这里插入图片描述


回到我们的线程等待,我们的pthread_join并不能像进程等待一样有非阻塞选项,这就说明一旦我们开始使用pthread_join开始等待新线程,如果新线程没有退出,我们的主线程就要一直被阻塞。


三、线程异常

当多线程进程执行时,无论哪个线程出现了异常,整个进程都会一起崩溃。

示例代码

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>

void *threadRun(void *args)
{
    printf("%s : %p\n", (char *)args, pthread_self());

    // 为更好观察,休息5s
    sleep(5);

    int a = 10;
    a /= 0;
    return (void *)10;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");
	sleep(5);
    while (1);
    return 0;
}

在这里插入图片描述

四、线程退出

在多线程进程中,我们在代码中应该尽量不使用exit()退出,因为exit是进程退出,任何一个线程调用此函数,都会使整个进程退出!

而如果我们想线程退出而不是进程退出,我们可以使用pthread_exit。

man 3 pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);

参数void* retval就是用于代替回调函数的返回值。

示例代码

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>

void *threadRun(void *args)
{
    printf("%s : %p\n", (char *)args, pthread_self());

    // 为更好观察,休息5s
    sleep(5);

    pthread_exit((void*) 11);

    return (void *)10;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");
	sleep(5);
    int ret;
    pthread_join(tid, (void**)&ret);
    std::cout << "main thread: " << ret << std::endl;
    sleep(5);
    return 0;
}

在这里插入图片描述

五、线程取消

man 3 pthread_cancel
#include <pthread.h>
int pthread_cancel(pthread_t thread);
Compile and link with -pthread.

返回值:成功返回0;失败返回错误码

调用此函数,可以取消tid为thread的线程。

需要注意的是如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_
CANCELED。

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>

void *threadRun(void *args)
{
    printf("%s : %p\n", (char *)args, pthread_self());

    // 为更好观察,休息5s
    sleep(5);

    // pthread_cancel(pthread_self());

    return (void *)10;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");
    sleep(5);
    pthread_cancel(tid);

    int ret;
    pthread_join(tid, (void **)&ret);

    std::cout << "PTHREAD_CANCELED : " << (int)PTHREAD_CANCELED << std::endl;
    std::cout << "main thread: " << ret << std::endl;
    sleep(5);
    return 0;
}

在这里插入图片描述

六、线程分离

当我们并不关心新线程退出后的退出码,不想使用pthread_join来阻塞主线程,又不想让退出后的新线程因为没有被等待导致内存空间泄露的问题,我们就可以让新线程进行分离,分离后,子线程退出时将自动释放资源。

man 3 pthread_detach
#include <pthread.h>
int pthread_detach(pthread_t thread);
Compile and link with -pthread.

一般调用此函数都是在新线程,让新线程自己分离自己,不过也可以分离别人。

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>
#include <string.h>

void *threadRun(void *args)
{
    pthread_detach(pthread_self());
    printf("%s : %p\n", (char *)args, pthread_self());
    sleep(5);
    return (void *)10;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");
    sleep(3);
    int ret;
    int n = pthread_join(tid, (void **)&ret);

    std::cout << "PTHREAD_CANCELED : " << (int)PTHREAD_CANCELED << std::endl;
    std::cout << "main thread: " \
              << "n : " << n << " error: " << strerror(n) << std::endl;
    sleep(5);
    return 0;
}

pthread_join失败
在这里插入图片描述

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

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

相关文章

ttkbootstrap界面美化系列之主窗口(二)

一&#xff1a;创建主窗口 在利用ttkbootstrap构建应用程序时&#xff0c;可以用tkinter传统的tk方法来创建主界面&#xff0c;也可以用ttkbootstrap中的window类来创建&#xff0c;下面我们来看看两者的区别 1&#xff0c;传统方法创建主界面 import tkinter as tk import …

力扣思路题:最长特殊序列1

int findLUSlength(char * a, char * b){int alenstrlen(a),blenstrlen(b);if (strcmp(a,b)0)return -1;return alen>blen?alen:blen; }

[CVPR-24] Text-to-3D using Gaussian Splatting

3DGS对初始化敏感&#xff1b;引入基于Point-E的3D SDS可以缓解多脸问题&#xff1b;外观细化阶段可以有效抑制异常点&#xff0c;并提高可视化效果&#xff1b;不需要对SDS的改进&#xff0c;用gudiance scale100可以取得很不错的结果。 [pdf | proj | code] 方法 Geometry O…

Linux——动静态库的制作及使用与动态库原理

目录 一、静态库 1.静态库的制作 2.静态库的使用 加载静态库方法一&#xff1a;安装头文件与库文件 加载静态库方法二&#xff1a;指定文件目录 二、动态库 1.动态库的制作 2.动态库的使用 方法一&#xff1a;安装到系统中 方法二&#xff1a;软链接 方法三&…

c语言文件操作(中)

目录 1. 文件的顺序读写1.1 顺序读写函数1.2 顺序读写函数的原型和介绍 结语 1. 文件的顺序读写 1.1 顺序读写函数 函数名功能适用于fgetc字符输入函数所有输出流fputc字符输出函数所有输出流fgets文本行输入函数所有输出流fputs文本行输出函数所有输出流fscanf格式化输入函数…

刷题DAY24 | LeetCode 77-组合

1 回溯法理论基础 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。回溯是递归的副产品&#xff0c;只要有递归就会有回溯。 所以以下讲解中&#xff0c;回溯函数也就是递归函数&#xff0c;指的都是一个函数。 1.1 回溯法的效率 回溯法的性能如何呢&#xff0…

完整指南:如何使用 Stable Diffusion API

Stable Diffusion 是一个先进的深度学习模型&#xff0c;用于创造和修改图像。这个模型能够基于文本描述来生成图像&#xff0c;让机器理解和实现用户的创意。使用这项技术的关键在于掌握其 API&#xff0c;通过编程来操控图像生成的过程。 在探索 Stable Diffusion API 的世界…

HarmonyOS NEXT应用开发之Web获取相机拍照图片案例

介绍 本示例介绍如何在HTML页面中拉起原生相机进行拍照&#xff0c;并获取返回的图片。 效果预览图 使用说明 点击HTML页面中的选择文件按钮&#xff0c;拉起原生相机进行拍照。完成拍照后&#xff0c;将图片在HTML的img标签中显示。 实现思路 添加Web组件&#xff0c;设置…

Vue.js+SpringBoot开发食品生产管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 加工厂管理模块2.2 客户管理模块2.3 食品管理模块2.4 生产销售订单管理模块2.5 系统管理模块2.6 其他管理模块 三、系统展示四、核心代码4.1 查询食品4.2 查询加工厂4.3 新增生产订单4.4 新增销售订单4.5 查询客户 五、…

生成式人工智能服务安全基本要求实务解析

本文尝试明晰《基本要求》的出台背景与实践定位&#xff0c;梳理《基本要求》所涉的各类安全要求&#xff0c;以便为相关企业遵循执行《基本要求》提供抓手。 引言 自2022年初以来&#xff0c;我国陆续发布算法推荐、深度合成与生成式人工智能服务相关的规范文件&#xff0c;…

阿里云服务器ECS经济型e实例性能如何?

阿里云服务器ECS推出经济型e系列&#xff0c;经济型e实例是阿里云面向个人开发者、学生、小微企业&#xff0c;在中小型网站建设、开发测试、轻量级应用等场景推出的全新入门级云服务器&#xff0c;CPU采用Intel Xeon Platinum架构处理器&#xff0c;支持1:1、1:2、1:4多种处理…

JS第一阶段1

文章目录 1. js组成2. JS三种书写位置JS输出语句 3. 变量4. 数据类型Number字符串型 String布尔型booleanUnddefined和Null 5. 获取变量的数据类型获取检测变量的数据类型 6. 数据转换类型转换为字符串转换为数字型&#xff08;重点&#xff09;转换为布尔型 7.运算符算数运算符…

找不到msvcp110.dll怎么办,msvcp110.dll丢失的5种修复方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp110.dll丢失”。由于msvcp110.dll是Microsoft Visual C Redistributable Package的重要组成部分&#xff0c;它的缺失会导致依赖于该组件的软件无法正常启动或运行&#xff0c;比如某…

从初学者到专家:Java反射的完整指南

一.反射的概念及定义 Java 的反射&#xff08; reflection &#xff09;机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&#xff0c;既然能拿到那么&#x…

Jenkins + Docker + ASP.NET Core自动化部署

本来没想着要写这篇博客&#xff0c;但是在实操过程中&#xff0c;一个是被网络问题搞炸了心态&#xff08;真心感觉网络能把人搞疯&#xff0c;别人下个包、下个镜像几秒钟搞定&#xff0c;我看着我的几KB小水管真是有苦说不出&#xff09;&#xff0c;另一个就是这里面坑还是…

C语言---指针的两个运算符:点和箭头

目录 点&#xff08;.&#xff09;运算符箭头&#xff08;->&#xff09;运算符需要注意实际例子 C语言中的指针是一种特殊的变量&#xff0c;它存储了一个内存地址。点&#xff08;.&#xff09;和箭头&#xff08;->&#xff09;是用于访问结构体和联合体成员的运算符。…

手撕算法-二叉树的镜像

题目描述 操作给定的二叉树&#xff0c;将其变换为源二叉树的镜像。数据范围&#xff1a;二叉树的节点数 0≤_n_≤1000 &#xff0c; 二叉树每个节点的值 0≤_val_≤1000要求&#xff1a; 空间复杂度 O(n) 。本题也有原地操作&#xff0c;即空间复杂度 O(1) 的解法&#xff0c…

【设计模式】-工厂模式

工厂模式是一种创建型设计模式&#xff0c;它提供了一种在不指定具体类的情况下创建对象的方法。工厂模式的核心思想是将对象的创建与使用分离&#xff0c;降低系统的耦合度&#xff0c;使系统更加灵活、可扩展。 工厂模式主要分为三种类型&#xff1a;简单工厂模式、工厂方法…

苍穹外卖-day06:HttpClient、微信小程序开发、微信登录(业务流程)、导入商品浏览功能代码(业务逻辑)

苍穹外卖-day06 课程内容 HttpClient微信小程序开发微信登录导入商品浏览功能代码 功能实现&#xff1a;微信登录、商品浏览 微信登录效果图&#xff1a; 商品浏览效果图&#xff1a; 1. HttpClient 1.1 介绍 HttpClient 是Apache Jakarta Common 下的子项目&#xff0c;…

苍穹外卖-day04:项目实战-套餐管理(新增套餐,分页查询套餐,删除套餐,修改套餐,起售停售套餐)业务类似于菜品模块

苍穹外卖-day04 课程内容 新增套餐套餐分页查询删除套餐修改套餐起售停售套餐 要求&#xff1a; 根据产品原型进行需求分析&#xff0c;分析出业务规则设计接口梳理表之间的关系&#xff08;分类表、菜品表、套餐表、口味表、套餐菜品关系表&#xff09;根据接口设计进行代…