Linux多线程之线程控制

(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~icon-default.png?t=N7T8https://blog.csdn.net/ky233?type=blog

点个关注不迷路⌯'▾'⌯

目录

一、pthread_crate

二、pthread_join

三、pthread_exit和pthread_cancel

四、关于线程id的探索

五、pthread_self

六、线程的局部存储

七、线程调用execl

八、分离线程


一、pthread_crate

大部分上篇文章已经详细说过了,这里仅做补充

在多进程中,我们不知道父子进程谁先运行,同样的,多线程也是随机的

线程异常的情况下

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

using namespace std;

void *threadRoutine(void *args)
{
    while (1)
    {
        cout << "新线程:" << (char *)args << "running..." << endl;
        sleep(1);
        int a = 100;
        a/=0;
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
    while (1)
    {
        cout << "main线程:"
             << "running..." << endl;
        sleep(1);
    }
}

这个时候我们就会发现,程序异常了,出现了8号信号,所以我们可以得出结论

线程一旦异常,都可能导致进程整体退出。

二、pthread_join

线程在创建并执行的时候,线程也是要进行等待的!如果主线程不等待,即会引起类似于进程的僵尸问题,导致内存泄漏!

int pthread_join(pthread_t thread,void **retval)
  • 参数一:线程id
  • 参数二:输出型参数,用于获取次线程的退出结果,如果不关心,可以传递 nullptrr
  • 返回值:失败返回错误码,成功为0

在调用了之后,我们的主线程会默认的阻塞,并等待新线程退出

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

using namespace std;

void *threadRoutine(void *args)
{
    int i = 0;
    while (1)
    {
        cout << "新线程:" << (char *)args << "running..." << endl;
        sleep(1);
        if (i++ == 10)
            break;
    }
    cout<<"新线程退出"<<endl;
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
    pthread_join(tid, nullptr);
    cout << "主线程正在等待...主线程成功退出" << endl;
}

在新线程退出的时候,我们可以返回特定的值,但是需要强转一下,如(void*)10,那么这是返回给谁呢?答案是谁等你就给谁,一般是给主线程的,所以我们可以这样写来获取新线程的返回值

void *threadRoutine(void *args)
{
    int i = 0;
    while (1)
    {
        cout << "新线程:" << (char *)args << "running..." << endl;
        // sleep(1);
        if (i++ == 10)
            break;
    }
    cout << "新线程退出" << endl;
    return (void *)10;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
    void *ret = nullptr;
    pthread_join(tid, &ret);
    cout << "主线程正在等待...主线程成功退出,新线程的返回值:" << (long long)ret << endl;
}

线程等待是不需要关心是否有异常的,因为新线程崩溃了,主线程也崩溃了

三、pthread_exit和pthread_cancel

线程终止的话是不能用exit的,这个是终止进程的,一旦调用,exit直接把进程终止了

void pthread_exit(void *retval);
  • 参数一:用于传递线程退出时的信息

或者我们可以用这个函数进行线程取消

int pthread_cancel(pthread_t thread)
  • 参数一:线程id
  • 如果想取消谁调用这个函数填入对应的线程id就可以了
  • void *threadRoutine(void *args)
    {
        while (1)
        {
            cout << "新线程:" << (char *)args << "running..." << endl;
            sleep(1);
            
        }
        cout << "新线程退出" << endl;
    }
    int main()
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
        int count=0;
        while (1)
        {
            cout << "main线程:"<< "running..." << endl;
            sleep(1);
            count++;
            if(count==5) break;
        }
        pthread_cancel(tid);
        cout<<"线程取消"<<tid<<endl;
        void *ret = nullptr;
        pthread_join(tid, &ret);
        cout << "主线程正在等待...主线程成功退出,新线程的返回值:" << (long long)ret << endl;
    }

这其中有几个细节需要注意

  •  线程被取消join的时候,退出码是-1,
  • 一般是在保证新线程运行起来了,后来不需要了才需要用这个接口
  • 不要用这个接口,用新线程取消主线程 

四、关于线程id的探索

当我们打印一个线程id之后可以发现,线程id是这个样子的,是一个非常大的整数

原因是因为它本质是一个地址!

上篇文章说过pthread会个线程提供一个用户层的栈结构,这个线程id就是栈结构的起始地址,对应的就是在库内部对应的相关属性的起始地址。

对于主线程来说直接用内核级栈结构,对于新线程来说则用的是共享区内部提供的用户层栈结构,这样就可以保证每个线程的栈是独立的了,并且还不和但执行流的进程相冲突

五、pthread_self

这个接口很简单,就是哪个线程掉的我,直接就获取对应线程的线程id

void *threadRoutine(void *args)
{
    while (1)
    {
        cout << "新线程:" << (char *)args << "running..." << pthread_self() << endl;
        sleep(1);
    }
    cout << "新线程退出" << endl;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
    printf("%lu,%p", tid, tid);
    cout << endl;
    int count = 0;
    while (1)
    {
        cout << "main线程:"
             << "running..." << pthread_self() << endl;
        sleep(1);
        count++;
        if (count == 5)
            break;
    }
    pthread_cancel(tid);
    cout << "线程取消" << tid << endl;
    void *ret = nullptr;
    pthread_join(tid, &ret);
    cout << "主线程正在等待...主线程成功退出,新线程的返回值:" << (long long)ret << endl;
}

注意:这里不推荐使用pthread_cancel调用pthread_self来自己取消自己

六、线程的局部存储

在进程中,两个进程调用同一个变量会发生写时拷贝,但是在线程中却不是这样的

int g_val = 0;

void *threadRoutine(void *args)
{
    while (1)
    {
        cout << (char *)args << " : " << g_val++ << " &: " << &g_val << endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
    while (1)
    {
        cout << "新线程: " << g_val << "&" << &g_val << endl;
        sleep(1);
    }
}

我们的地址是一样的,所以我们的全局变量是被多线程共享的

但是也可以变成私有的,只需要在全局变量前加上__thread就可以了如:

__thread int g_val=0;

我们就可以看到每个线程都有属于自己的全局变量了

  • __thread:修饰全局变量,带来的结果就是让每一个线程各自拥有一个全局变量--线程的局部存储

七、线程调用execl

在进程时调用这个函数只是把内存中和磁盘上的数据替换掉,那么在线程中就是直接将我们所对应的代码和数据全部替换!会影响其他线程,把其他线程终止,然后直接就去调用替换的程序了,就等同于这个进程调用execl进行程序替换

八、分离线程

在进行join时,我们的主线程是必须要的等待的,也可以看到OS并没有给我们更多的选项,可是如果我们不想等待呢?那么我们就可以进行我们的分离线程

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
  • 参数一:线程id

用法如下

__thread int g_val = 0;

void *threadRoutine(void *args)
{
    pthread_detach(pthread_self());
    while (1)
    {
        cout << (char *)args << " : " << g_val++ << " &: " << &g_val << endl;
        sleep(1);
        break;
    }
    pthread_exit((void *)10);
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
    while (1)
    {
        cout << "新线程: " << g_val << "   &" << &g_val << endl;
        sleep(1);
        break;
    }
    long long n = pthread_join(tid, nullptr);
    cout << "n:" << n << "错误码" << strerror(n) << endl;
}

如果我们强行join的话,就会产生报错,错误码是非法的参数

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RAG、数据隐私、攻击方法和安全提示

原文地址&#xff1a;RAG, Data Privacy, Attack Methods & Safe-Prompts 最近的一项研究探讨了 RAG 安全漏洞以及通过检索数据集访问私有数据的方式。还讨论了防御和安全提示工程示例。 介绍 RAG 在构建生成式 AI 应用程序中非常受欢迎。RAG 在生成式 AI 应用中采用的原因…

Elasticsearch架构原理

一. Elasticsearch架构原理 1、Elasticsearch的节点类型 在Elasticsearch主要分成两类节点&#xff0c;一类是Master&#xff0c;一类是DataNode。 1.1 Master节点 在Elasticsearch启动时&#xff0c;会选举出来一个Master节点。当某个节点启动后&#xff0c;然后使用Zen D…

指针数组和数组指针(详细解释)

指针数组 指针数组的作用 指针数组和数组指针是C语言中常用的概念&#xff0c;它们分别有不同的作用和用法。 指针数组&#xff1a; 指针数组是一个数组&#xff0c;其中的每个元素都是指针类型。它可以用来存储多个指针&#xff0c;每个指针可以指向不同的数据类型或者相同…

Pytorch学习 day08(最大池化层、非线性激活层、正则化层、循环层、Transformer层、线性层、Dropout层)

最大池化层 最大池化&#xff0c;也叫上采样&#xff0c;是池化核在输入图像上不断移动&#xff0c;并取对应区域中的最大值&#xff0c;目的是&#xff1a;在保留输入特征的同时&#xff0c;减小输入数据量&#xff0c;加快训练。参数设置如下&#xff1a; kernel_size&#…

类与对象-对象特性

师从黑马程序员 对象的初始化和清理 构造函数和析构函数 用于完成对象的初始化和清理工作 如果我们不提供构造和析构&#xff0c;编译器会提供编译器提供的构造函数和析构函数是空实现 构造函数&#xff1a;主要用于创建对象时为对象的成员属性赋值&#xff0c;构造函数由编…

了解华为(PVID VLAN)与思科的(Native VLAN)本征VLAN的区别并学习思科网络中二层交换机的三层结构局域网VLAN配置

一、什么是二层交换机&#xff1f; 二层交换机&#xff08;Layer 2 Switch&#xff09;是一种网络设备&#xff0c;主要工作在OSI模型的数据链路层&#xff08;第二层&#xff09;&#xff0c;用于在局域网内部进行数据包的交换和转发。二层交换机通过学习MAC地址表&#xff0…

毅速3D打印随形透气钢:模具困气排气革新之选

在注塑生产过程中&#xff0c;模具内的气体若无法有效排出&#xff0c;往往会引发困气现象&#xff0c;导致产品表面出现气泡、烧焦等瑕疵。这些瑕疵不仅影响产品的美观度&#xff0c;更可能对其性能造成严重影响&#xff0c;甚至导致产品报废&#xff0c;从而增加生产成本。 传…

政安晨:【深度学习处理实践】(四)—— 实施一个温度预测示例

在开始使用像黑盒子一样的深度学习模型解决温度预测问题之前&#xff0c;我们先尝试一种基于常识的简单方法。 它可以作为一种合理性检查&#xff0c;还可以建立一个基准&#xff0c;更高级的机器学习模型需要超越这个基准才能证明其有效性。对于一个尚没有已知解决方案的新问…

Linux之生产消费者模型

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 我们在条件满足的时候&#xff0c;唤醒指定的线程&a…

超越Chain-of-Thought LLM 推理

原文地址&#xff1a;Beyond Chain-of-Thought LLM Reasoning 2024 年 2 月 13 日 介绍 最近的一项研究解决了需要增强大型语言模型 (LLM) 的推理能力&#xff0c;超越直接推理 (Direct Reasoning&#xff0c;DR) 框架&#xff0c;例如思想链和自我一致性&#xff0c;这些框架可…

分割模型TransNetR的pytorch代码学习笔记

这个模型在U-net的基础上融合了Transformer模块和残差网络的原理。 论文地址&#xff1a;https://arxiv.org/pdf/2303.07428.pdf 具体的网络结构如下&#xff1a; 网络的原理还是比较简单的&#xff0c; 编码分支用的是预训练的resnet模块&#xff0c;解码分支则重新设计了。…

HTML入门:属性

你好&#xff0c;我是云桃桃。今天来聊一聊 HTML 属性写法和特点。 HTML 属性是用于向 HTML 标签&#xff08;也叫 HTML 元素&#xff09;提供附加信息或配置的特性。 如果说&#xff0c;把HTML 标签比作一个房子&#xff0c;HTML 标签定义了房子的结构和用途&#xff0c;比如…

基于SpringBoot的闲置房屋搜索平台设计与实现

目 录 摘 要 I Abstract II 引 言 1 1相关技术 3 1.1 jQuery技术简介 3 1.2 SpringBoot框架简介 3 1.3 Bootstrap框架简介 4 1.4 ECharts框架简介 4 1.5 百度地图API简介 4 1.6 Ajax技术简介 5 1.7 MySQL数据库简介 5 1.8本章小结 6 2系统分析 7 2.1功能需求 7 2.2非功能需求 …

微软财务GPT Excel Copilot for Finance使用攻略

功能本身不收费&#xff0c;但是这个功能需要微软的商业版office账号才能使用&#xff0c;如果你没有账号&#xff0c;可以直说。 在桌面Excel软件中登录账号后&#xff0c;点击“copilot for finance”按钮&#xff0c;如果没有出现&#xff0c;则点击“加载项”&#xff0c;…

2024 年中国高校大数据挑战赛赛题 D:行业职业技术培训能力评价完整思路以及源代码分享

中国是制造业大国&#xff0c;产业门类齐全&#xff0c;每年需要培养大量的技能娴 熟的技术工人进入工厂。某行业在全国有多所不同类型&#xff08;如国家级、 省级等&#xff09;的职业技术培训学校&#xff0c;进行 5 种技能培训。学员入校时需要 进行统一的技能考核&#xf…

简述epoll实现

所有学习笔记&#xff1a;https://github.com/Dusongg/StudyNotes 文章目录 epoll数据结构的选择&#xff1f;以tcp为例&#xff0c;网络io的可读可写如何判断&#xff1f;epoll如何做到线程安全&#xff1f;LT和ET如何实现&#xff1f;tcp状态和io的读写有哪些关系&#xff1…

文本生成视频:从 Write-a-video到 Sora

2024年2月15日&#xff0c;OpenAI 推出了其最新的文本生成视频模型——Sora。Sora 能够根据用户的指令生成一分钟长度的高质量视频内容。这一创新的发布迅速在社会各界引发了广泛关注与深入讨论。本文将围绕本实验室发表于SIGGRAPH AISA 的 Write-a-video和 Sora 展开&#xff…

CPU设计实战-协处理器访问指令的实现

目录 一 协处理器的作用与功能 1.计数寄存器和比较寄存器 2.Status寄存器 3.Cause寄存器(标号为13) 4.EPC寄存器(标号为14) 5.PRId寄存器(标号为15) 6.Config 寄存器(标号为16)-配置寄存器 二 协处理器的实现 三 协处理器访问指令说明 四 具体实现 1.译码阶段 2.执行…

git命令行提交——github

1. 克隆仓库至本地 git clone 右键paste&#xff08;github仓库地址&#xff09; cd 仓库路径&#xff08;进入到仓库内部准备提交文件等操作&#xff09; 2. 查看main分支 git branch&#xff08;列出本地仓库中的所有分支&#xff09; 3. 创建新分支&#xff08;可省…

Edu18 -- Divide by Three --- 题解

目录 Divide by Three&#xff1a; 题目大意&#xff1a; ​编辑​编辑思路解析&#xff1a; 代码实现&#xff1a; Divide by Three&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 一个数字是3的倍数&#xff0c;那么他的数位之和也是3的倍数&#xff0c;所以我…