【Linux操作系统】线程控制

目录

  • 一、线程创建
  • 二、线程等待
  • 三、线程退出
  • 四、线程的优缺点
  • 五、多线程的创建
  • 六、C++11的多线程
  • 七、线程分离

一、线程创建

使用接口pthread_create创建新线程,头文件是pthread.h
在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;

void* newthreadrun(void* arg)
{
    while(true)
    {
        cout << "I am new thread" << endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthreadrun, nullptr);

    while(true)
    {
        cout << "I am main thread" << endl;
        sleep(1);
    }
    return 0;
}

主线程和新线程的pid是一样的,因为属于同一个进程;要查看线程的id,可以获取变量tid打印出来,这个是新线程的id返回给主线程;如果要像getpid一样获取自己的id,可以使用接口pthread_self
在这里插入图片描述

string Tohex(pthread_t tid)
{
    char id[1024];
    snprintf(id, sizeof(id), "0x%lx", tid);
    return id;
}
void* newthreadrun(void* arg)
{
    int cnt = 5;
    while(cnt)
    {
        cout << "I am new thread, cnt: "<<cnt <<" pid: "<< getpid()<<" threadid: "<<
        Tohex(pthread_self())<< endl;
        sleep(1);
        cnt--;
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthreadrun, nullptr);

    int cnt = 10;
    while(cnt)
    {
        cout << "I am main thread, cnt: "<< cnt <<" pid: "<<getpid()<< " tid: "<<
        Tohex(tid)<<" threadid: "<<Tohex(pthread_self())<<endl;
        sleep(1);
        cnt--;
    }
    return 0;
}

在这里插入图片描述
创建线程的接口的第四个参数可以传参,传入一个字符串作为新线程的名字,注意类型转换。
在这里插入图片描述
在这里插入图片描述
新线程与主线程谁先运行?不确定,由调度器决定
如果主线程先退出,那么其他的线程都要退出,因为主线程等于进程,进程退出了所有的资源要释放,而所有的线程是共享这些资源的。

把主线程的cnt改为1秒,1秒后新线程也退出
在这里插入图片描述
所以一般情况下,往往是主线程最后一个退出,线程也要wait,否则会出现资源泄漏问题。

二、线程等待

使用线程等待的接口:
在这里插入图片描述
主线程进行等待,直到其他线程结束才退出

代码:

	int n = pthread_join(tid, nullptr);
    cout << "主线程退出..., n = " << n << endl;
    sleep(4);

在这里插入图片描述
第二个参数可以获取线程的退出信息
在这里插入图片描述
在这里插入图片描述

三、线程退出

一个进程里面的资源,是所有线程可以共享的。定义一个全局变量,主线程和新线程都可以看到。

int g_val = 100;
void *newthreadrun(void *args)
{
    string threadname = (char *)args;
    while (true)
    {
        printf("g_val: %d, &g_val: %p\n", g_val, &g_val);
        sleep(1);
        g_val++;
    }
    return (void *)123;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthreadrun, (void *)"thread-1");
    while (true)
    {
        printf("g_val: %d, &g_val: %p\n", g_val, &g_val);
        sleep(1);
    }
    void *ret = nullptr;
    int n = pthread_join(tid, &ret);
    cout << "主线程退出..., n = " << n << " ret: " << (long long)ret << endl;
    sleep(4);
    return 0;
}

在这里插入图片描述
如果任何一个线程出现问题,比如出现了野指针,那么整个进程都会被终止

void *newthreadrun(void *args)
{
    string threadname = (char *)args;
    int cnt = 1;
    while (true)
    {
        printf("g_val: %d, &g_val: %p\n", g_val, &g_val);
        sleep(1);
        if(cnt == 5)
        {
            int* p = nullptr;
            *p = 10;
        }
        g_val++;
        cnt++;
    }
    return (void *)123;
}

在这里插入图片描述
野指针错误是异常信号,也就是说,一旦产生信号,整个进程都会被干掉。

线程退出有三种方式:return、pthread_exit、pthread_cancel
1️⃣return
返回退出码
在这里插入图片描述

2️⃣pthread_exit
在这里插入图片描述
在新线程中设置pthread_exit
在这里插入图片描述
3秒后新线程退出,主线程还在
在这里插入图片描述

3️⃣pthread_cancel
在这里插入图片描述
指定新线程3秒后退出,主线程继续运行
在这里插入图片描述
在这里插入图片描述

四、线程的优缺点

优点:

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量
  • 在等待慢速 I/O 操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O 密集型应用,为了提高性能,将 I/O 操作重叠。线程可以同时等待不同的I/O 操作

缺点:

  • 性能损失。一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
  • 健壮性降低。编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的
  • 缺乏访问控制。进程是访问控制的基本粒度,在一个线程中调用某些 OS 函数会对整个进程造成影响

为什么线程切换与进程切换相比需要操作系统做到工作少了很多?
在这里插入图片描述
进程运行时,CPU读取进程的上下文信息,用内部的寄存器进行读取。有一个寄存器CR3是指向页表的首地址。读取方式:用地址空间的虚拟地址通过页表映射得到物理内存的数据,然后交给CPU。

线程是进程的其中一个执行流,CPU也要做相同类似的工作,线程切换也要保存上下文数据,只是比进程少一点。进程切换,每次CPU处理某条代码都要到内存中读取,得到数据后,到下一条代码又要做同样的工作,这样效率就低了一些。而线程也要到内存中读取,但是在CPU中,有一个硬件设备叫cache,作用是可以将当前读到的代码的周围其他代码和数据也一并交给CPU,这样下次遇到时,就可以直接在CPU处理了,效率提高了一些,工作也就少了。

那为什么线程可以这样,进程不行呢?因为进程切换时CPU内部的cache的上下文数据也要跟着切换,即清空掉然后换新的一批。线程不一样是因为多个线程在一个进程内部,每个线程是一个进程的其中一个执行流,进程的所有资源(地址空间、页表、内存)都是可以给这些线程共享的,所以线程切换时,cache里面的上下文数据还在。

线程私有:

  1. 线程的硬件上下文(CPU寄存器的值)
  2. 线程的独立栈结构

线程共享:

  1. 代码和全局数据
  2. 进程文件描述符表

线程安全问题:一个线程出问题,导致其他线程也出问题,整个进程就会退出
公共函数被重入:多线程中,函数被多个线程同时进入

五、多线程的创建

用for循环5次,创建5个线程,然后打印它们的名字

const int threadnum = 5;
void* taskthread(void* args)
{
    char* threadname = static_cast<char*>(args);
    while(true)
    {
        cout << threadname << endl;
        sleep(2);
    }
    delete []threadname;
    return nullptr;
}
int main()
{
    vector<pthread_t> threads;
    for(int i=0;i<threadnum;i++)
    {
        char threadname[64];
        snprintf(threadname, sizeof(threadname), "thread-%d", i+1);

        pthread_t tid;
        pthread_create(&tid, nullptr, taskthread, threadname);
        threads.push_back(tid);
    }

    for(auto e:threads)
    {
        pthread_join(e, nullptr);
    }
    return 0;
}

在这里插入图片描述
发现后面都变成5了,因为这些线程用的是同一个缓冲区,所以后面的线程名字把前面的覆盖了。

修改:每个线程有自己的空间
在这里插入图片描述
在这里插入图片描述

pthread_create的第四个参数不仅可以传递整型变量,还可以传对象,自定义的类型

让线程完成加法:

class Task
{
public:
    Task()
    {}
    void SetData(int x, int y)
    {
        _x = x;
        _y = y;
    }
    int Excute()
    {
        return _x + _y;
    }
    ~Task()
    {}
private:
    int _x;
    int _y;
};
class DataThread
{
public:
    DataThread(int x, int y, const string& threadname)
        :_threadname(threadname)
    {
        _t.SetData(x, y);
    }
    int run()
    {
        return _t.Excute();
    }
    string getName()
    {
        return _threadname;
    }
    ~DataThread()
    {}
private:
    Task _t;
    string _threadname;
};
const int threadnum = 5;
void* taskthread(void* args)
{
    DataThread* td = static_cast<DataThread*>(args);
    string threadname = td->getName();
    int ret = td->run();
    cout << threadname<<" ret is: " <<ret<<endl;
    delete td;
    return nullptr;
}
int main()
{
    vector<pthread_t> threads;
    for(int i=0;i<threadnum;i++)
    {
        char threadname[64];
        snprintf(threadname, sizeof(threadname), "thread-%d", i+1);
        DataThread* td = new DataThread(10, 20, threadname);

        pthread_t tid;
        pthread_create(&tid, nullptr, taskthread, td);
        threads.push_back(tid);
    }

    for(auto e:threads)
    {
        pthread_join(e, nullptr);
    }
    return 0;
}

在这里插入图片描述

六、C++11的多线程

C++11的多线程是对原生线程的封装,为了语言的跨平台性

void newthread(int num)
{
    while (true)
    {
        cout << "I am new thread-" << num << endl;
        sleep(1);
    }
}
int main()
{
    thread t1(newthread, 1);
    thread t2(newthread, 2);
    thread t3(newthread, 3);
    while (true)
    {
        cout << "I am main thread-" << endl;
        sleep(1);
    }
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

在这里插入图片描述

对原生线程做封装:

#ifndef __THREAD_HPP__
#define __THREAD_HPP__
#include <iostream>
#include <string>
#include <vector>
#include <pthread.h>
#include <unistd.h>
#include <functional>

using namespace std;

namespace yss
{
    template <class T>
    using func_t = function<void(T &)>;

    template <class T>
    class Thread
    {
    public:
        void Excute()
        {
            _func(_data);
        }
        Thread(func_t<T> func, const T &data, const string &threadname)
            : _func(func), _data(data), _threadname(threadname), _stop(true)
        {
        }
		// 注意static
        static void *runthread(void *args) // 类成员函数,形参有this指针
        {
            Thread<T> *self = static_cast<Thread<T> *>(args);
            self->Excute();
            return nullptr;
        }
		// 创建
        bool Satrt()
        {
            int n = pthread_create(&_tid, nullptr, runthread, this);
            if (!n) // 创建成功
            {
                _stop = false;
                return true;
            }
            else
            {
                return false;
            }
        }
		// 分离
        void Detach()
        {
            if (!_stop)
            {
                pthread_detach(_tid);
            }
        }
		// 等待
        void Join()
        {
            if (!_stop)
            {
                pthread_join(_tid, nullptr);
            }
        }

        void Stop()
        {
        	_stop = true;
        }
        string name()
        {
            return _threadname;
        }

        ~Thread()
        {
        }

    private:
        pthread_t _tid;
        string _threadname;
        bool _stop;
        func_t<T> _func;
        T _data;
    };
}
#endif

在这里插入图片描述

七、线程分离

默认情况下,新创建的线程是 joinable 的,线程退出后,需要对其进行pthread_join 操作,否则无法释放资源,从而造成系统泄漏

如下是主线程等待新线程

void* newthread(void* args)
{
    int cnt = 5;
    while(cnt)
    {
        cout << "I am new thread" << endl;
        sleep(1);
        cnt--;
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthread, nullptr);

    cout << "thread wait block" << endl;
    pthread_join(tid, nullptr);
    cout << "thread wait return" << endl;
    return 0;
}

在这里插入图片描述

如果不要等待,必须对新线程进行分离(可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离)

接口:
在这里插入图片描述
分离的线程不需要等待,主线程可以做自己的事情

void* newthread(void* args)
{
    int cnt = 5;
    while(cnt)
    {
        cout << "I am new thread" << endl;
        sleep(1);
        cnt--;
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthread, nullptr);
    //分离新线程
    pthread_detach(tid);
    while(true)
    {
        cout << "I am main thread" << endl;
        sleep(1);
    }

    // cout << "thread wait block" << endl;
    // pthread_join(tid, nullptr);
    // cout << "thread wait return" << endl;
    return 0;
}

在这里插入图片描述

如果让主线程5秒结束,分离的线程死循环:

void* newthread(void* args)
{
    while(true)
    {
        cout << "I am new thread" << endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthread, nullptr);
    //分离新线程
    pthread_detach(tid);
    int cnt = 5;
    while(cnt--)
    {
        cout << "I am main thread" << endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
线程分离了,但是资源依然是共享的,主线程先退出,那么整个进程就会结束,分离的线程也同样退出。

如果分离的线程中的某个函数有异常(比如除0错误),整个进程会直接退出,并打印报错信息

void* newthread(void* args)
{
    while(true)
    {
        cout << "I am new thread" << endl;
        sleep(1);
        int a = 10;
        a /= 0;
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthread, nullptr);
    //分离新线程
    pthread_detach(tid);
    while(true)
    {
        cout << "I am new thread" << endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

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

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

相关文章

2024国赛数学建模-模拟火算法(MATLAB 实现)

模拟退火算法 1.1 算法原理 模拟退火算法的基本思想是从一给定解开始 ,从邻域 中随机产生另一个解 ,接受 Metropolis准则允许目标函数在 有限范围内变坏 ,它由一控制参数 t决定 ,其作用类似于物 理过程中的温度 T,对于控制参数的每一取值 ,算法持续进 行“产生 —判断 —接受…

部署Apache网站

简易部署自己的apache网站 写在前面&#xff1a;先安装好mysql&#xff0c;再来搭建站点 1.安装php [rootlocalhost ~]# yum install php -y ##安装了php&#xff0c;默认会和apache结合工作2.创建文件编写php网页代码 [rootlocalhost ~]# vim /var/www/html/index.php ##创…

uniapp交互反馈

页面交互反馈可以通过:uni.showToast(object)实现,常用属性有 ioc值说明 值说明success显示成功图标&#xff0c;此时 title 文本在小程序平台最多显示 7 个汉字长度&#xff0c;App仅支持单行显示。error显示错误图标&#xff0c;此时 title 文本在小程序平台最多显示 7 个汉字…

【C++进阶】hash表的封装

文章目录 hash表哈希表的关键组成部分哈希表的优缺点优点&#xff1a;缺点&#xff1a; 常见应用场景 开放定址法实现hash表负载因子 (Load Factor)负载因子的意义负载因子的影响再散列 (Rehashing)示例 整体框架insertFinderasehash桶封装框架insertfinderase~HashTable() 总结…

Apache ShardingSphere数据分片弹性伸缩加解密中间件

Apache ShardingSphere Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。 软件背景 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding…

git 提交自动带上storyid

公司里的运维团队的产品经理&#xff0c;那老六提出说要在每个提交带上的jira storyid或者bugid&#xff0c;不用他自己弄不顾他人麻烦&#xff0c;真想问候他的xx。不过既然已经成为定局&#xff0c;还是想想有没有其他办法。经一番调研&#xff0c;网上有比较零碎的信息&…

Nginx 负载均衡+高可用 集群部署(Keepalived+LVS DR模式)

一、LVS负载均衡简介 1.1 LVS基本介绍 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&#xff0c;是由章文嵩博士主导开发的开源负载均衡项目&#xff0c;目前LVS已经被集成在Linux内核中。该项目在Linux内核中实现了基于IP地址的请求数据负载均衡调度方…

以太网交换机工作原理学习笔记

在网络中传输数据时需要遵循一些标准&#xff0c;以太网协议定义了数据帧在以太网上的传输标准&#xff0c;了解以太网协议是充分理解数据链路层通信的基础。以太网交换机是实现数据链路层通信的主要设备&#xff0c;了解以太网交换机的工作原理也是十分必要的。 1、以太网协议…

CTFHub技能树-Git泄漏-Stash

目录 一、前提知识 1.什么是git stash 2.git文件目录结构 3.git中对象指向 二、解题过程 方法一&#xff1a;使用GitHack 方法二&#xff1a;使用Git_Extract工具&#xff0c;这个是自动解析不用git stash等操作&#xff0c;直接得到flag 当前大量开发人员使用git进行版本…

SQL进阶技巧:截止当前批次前的批次量与订单量 | 移动窗口问题

目录 0 场景描述 1 数据准备 2 问题分析 3 小结 0 场景描述 表A有如下字段,user id(用户ID),batch id(批次ID),order id(订单ID),create time(创建时间),同一个用户ID下有多个批次,同一个批次下有多个订单ID,相同批次ID的创建时间是相同的,创建时间精确到了秒。 统计,截…

1-10 图像增强对比度 opencv树莓派4B 入门系列笔记

目录 一、提前准备 二、代码详解 enhanced_image cv2.convertScaleAbs(image, alpha1.5, beta0) 三、运行现象 四、完整工程贴出 一、提前准备 1、树莓派4B 及 64位系统 2、提前安装opencv库 以及 numpy库 3、保存一张图片 二、代码详解 import cv2 # 增强图像的对比度 …

【音视频】播放音视频时发生了什么? 视频的编解码 H264是什么? MP4是什么?

目录 ✨播放一个视频的流程✨为什么要编码&#xff08;压缩&#xff09;视频数据&#xff1f;✨如何编码&#xff08;压缩&#xff09;数据&#x1f384;简单的例子&#x1f384;音视频编码方式&#x1f384;视频编码格式H264编码是什么&#xff1f;发展历程&#xff1f;H.264基…

【Python游戏开发】拼图小游戏demo

使用mu编辑器 pgzero编写拼图小游戏 import randomSIZE 96 # 设置每张图块的大小 WIDTH SIZE * 3 # 根据土块大小设置窗口 HEIGHT SIZE * 3 pics [] # 存放图块 finished False # 游戏结束标识# 将前八张图块存放在pics列表中 for i in range…

009.Python爬虫系列_urllib模块案例

我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈 入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈 虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈 PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈 Oracle数…

传统CV算法——基于Opencv的多目标追踪算法

基于 OpenCV 的跟踪算法有多种&#xff0c;每种算法都有其特定的应用场景和优缺点。以下是一些常见的基于 OpenCV 的目标跟踪算法&#xff1a; 1. BOOSTING 跟踪器 描述&#xff1a;基于 AdaBoost 算法的跟踪器。它是一种早期的跟踪算法&#xff0c;使用的是基于弱分类器的强…

php转职golang第二期

以下是一份简单的 Go 基本语法笔记&#xff1a; 变量与常量&#xff1a; • var 声明变量。• const 声明常量。数据类型&#xff1a; • 整型、浮点型、布尔型、字符串型等。流程控制&#xff1a; • if-else 语句。• for 循环。函数&#xff1a; • 定义和调用函数。数…

Linux-【组管理、权限管理、定时任务调度】

目录 前言 Linux组基本介绍 文件/目录 所有者 查看文件 所有者 修改文件所有者 文件/目录 所在组 修改文件/目录 所在组 其它组 改变用户所在组 权限的基本介绍 rwx权限 rwx作用到文件 rwx作用到目录 修改权限 第一种方式&#xff1a;、-、变更权限 第二种方式…

系统编程--线程

这里写目录标题 线程概念什么是线程简介图解 内核原理图解 线程共享资源与非共享资源共享资源非共享资源 线程优缺点 线程控制原语pthread_self、pthread_create简介代码总结 循环创建多个子线程错误代码 线程间全局变量共享pthread_exit简介代码 一级目录二级目录二级目录二级…

可持久化Trie详解,最大异或和,k大异或和

零、碎碎念 打比赛没遇上可持久化Trie&#xff0c;做个CMU 15-445的project0&#xff0c;上来就碰上了…… 关于Trie详见&#xff1a;[Trie树/字典树的原理及实现C/C]_trie字典树原理-CSDN博客 一、可持久化Trie 1.1 基本思想 可持久化Trie和可持久化线段树类似&#xff0c…

白小白为波司登新品创作歌曲《登峰之路》,穿越风雨守护前行者

随着天气渐凉&#xff0c;波司登品牌推出全新新品——轻薄羽绒叠变系列&#xff0c;作为波司登品牌的新品推荐官&#xff0c;歌手白小白为波司登创作并演唱《轻薄羽绒叠变》系列主题曲《登峰之路》。歌曲中&#xff0c;白小白以激昂澎湃&#xff0c;明快有力的旋律以及深情又充…