【Linux】线程操作

news2024/11/13 13:52:07

文章目录

  • 前言
  • 一、线程相关操作函数
    • 1. pthread_create
    • 2. pthread_join
    • 3. pthread_exit
    • 4. pthread_cancel
    • 5. pthread_detach
    • 6. 示例代码


前言

在 Linux 中并不存在真正意义上的线程, 而是通过复用进程的结构来实现的, 叫做轻量级进程. 线程是一个进程内部的一个执行流, 而一个进程最少都有一个线程, 就是该进程本身, 线程在进程内部运行,本质是在进程地址空间内运行, 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流.

进程是资源分配的基本单位, 线程是调度的基本单位.

一、线程相关操作函数

1. pthread_create

头文件: #include <pthread.h>
函数声明: int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

  • 返回值: 成功返回 0, 失败返回 errno.
  • thread: 输出型参数, 返回线程 id.
  • attr: 设置线程的属性, attr 为 NULL 表示使用默认属性.
  • start_routine: 函数指针, 该线程执行的函数.
  • arg: 传递给 start_routine 的参数.

功能: 创建一个线程, 该线程会执行 start_routine 函数.

示例代码(入口函数不传参):

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

void* ThreadRoutine(void* arg)
{
    for(int i = 0; i < 5; ++i)
    {
        cout << "Hello World" << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, nullptr);
    pthread_join(tid, nullptr);
    return 0;
}

运行结果:
在编译时会出现如下问题:
在这里插入图片描述
提示我们使用的函数是未定义的, 原因就在于使用了线程库提供的函数没有链接该库, 在 makefile 文件中指明库名即可, 如下:
在这里插入图片描述
此时再编译就没问题了, 运行结果如下:
在这里插入图片描述
程序运行后, 该线程执行流就会去执行其入口函数了, 通过监控脚本观察此时确实有两个线程:
在这里插入图片描述
可以看到监控显示的信息中, 有一个熟悉的字段 PID, 这个 PID 也就是我们跑起来的一个进程, 而字段 LWP(Light Weight Process) 表示轻量级进程, 可以得知在该进程中存在两个执行流, 也就是两个线程, 而其中一个线程的 LWP 和 PID 是一致的, 该线程就是我们的主线程, 此前我们认为的一个进程在 Linux 中其实就是一个只有一个执行流的进程, 所以说在 Linux 中并不存在真正意义上的线程, 在一个进程的内部可以存在多个线程, 并且每个线程都共享所处进程的部分数据, 大致如下图所示:
在这里插入图片描述
整个图表示一个进程, 里面的各个 task_struct 表示一个线程.

示例代码(入口函数传递内置类型):

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

void* ThreadRoutine(void* arg)
{
    const char* buf = static_cast<const char*>(arg);
    for(int i = 0; i < 5; ++i)
    {
        cout << buf << "Hello World" << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    char buf[] = "[wcm]:";
    pthread_create(&tid, nullptr, ThreadRoutine, buf);
    pthread_join(tid, nullptr);
    return 0;
}

运行结果:
在这里插入图片描述
入口函数的参数类型为 void*, 表示我们可以传递任意类型的参数, 在入口函数中使用时, 只需要进行类型转换即可, 返回值也是一样的.

2. pthread_join

头文件: #include <pthread.h>
函数声明: int pthread_join(pthread_t thread, void **retval);

  • 返回值: 成功返回 0, 失败返回 errno.
  • thread: 等待的线程 id.
  • retval: 被等待线程的返回值.

功能: 主线程阻塞式的等待回收一个线程.

实例代码:

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

void* ThreadRoutine(void* arg)
{
    for(int i = 0; i < 5; ++i)
    {
        cout << "Hello World" << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, nullptr);
    pthread_join(tid, nullptr);
    return 0;
}

如果 pthread_join 的第二个参数设置为空, 表示不接收等待线程的返回值, 要想接收返回值, 代码如下:

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

void* ThreadRoutine(void* arg)
{
    const char* buf = static_cast<const char*>(arg);
    for(int i = 0; i < 5; ++i)
    {
        cout << buf << "Hello World" << endl;
        sleep(1);
    }
    return (void*)111;
}

int main()
{
    pthread_t tid;
    char buf[] = "[wcm]:";
    pthread_create(&tid, nullptr, ThreadRoutine, buf);
    void* ret;
    pthread_join(tid, &ret);
    int res = reinterpret_cast<uint64_t>(ret);
    sleep(1);
    cout << "exit code: " << res << endl;
    return 0;
}

运行结果:
在这里插入图片描述

3. pthread_exit

头文件: #include <pthread.h>
函数声明: void pthread_exit(void *retval);

  • retval: 线程的返回值.

功能: 如果在一个线程内使用 exit 来终止该线程, 那么该进程所属的整个进程也会被终止, pthread_exit 只会终止当前线程, 并不会影响进程乃至其他任何线程执行流.

示例代码:

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

void* ThreadRoutine(void* arg)
{
    const char* buf = static_cast<const char*>(arg);
    for(int i = 0; i < 5; ++i)
    {
        if(i == 3)
        {
            exit(1);
        }
        cout << buf << "Hello World" << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    char buf[] = "[wcm]:";
    pthread_create(&tid, nullptr, ThreadRoutine, buf);
    pthread_join(tid, nullptr);
    sleep(10);
    cout << "main() quit" << endl;
    return 0;
}

先看看线程通过 exit 终止会怎样:
在这里插入图片描述
可以看到, 通过 exit 终止的线程不止本身被终止了, 连带着整个进程都被终止了, 原因也很简单, exit 本身就是终止一个进程的, 而多个线程同属一个进程中, 如果该线程的入口函数中通过 exit 终止了, 那么理所应当的整个进程都会被终止, 而 pthread_exit 只会终止调用线程, 并且还可以携带返回值:

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

void* ThreadRoutine(void* arg)
{
    const char* buf = static_cast<const char*>(arg);
    for(int i = 0; i < 5; ++i)
    {
        if(i == 3)
        {
            char* buf = new char[64];
            snprintf(buf, 64, "i am return val");
            pthread_exit(buf);
        }
        cout << buf << "Hello World" << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    char buf[] = "[wcm]:";
    pthread_create(&tid, nullptr, ThreadRoutine, buf);
    void* ret;
    pthread_join(tid, &ret);
    const char* res = static_cast<const char*>(ret);
    cout << res << endl;
    return 0;
}

运行结果:
在这里插入图片描述
成功收到线程终止后的返回值, 并且该线程的终止不会影响其他线程.

4. pthread_cancel

头文件: #include <pthread.h>
函数声明: int pthread_cancel(pthread_t thread);

  • 成功返回 0, 失败返回 errno.
  • thread: 被取消的线程 id.

功能: 取消一个线程的执行.

示例代码:

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

void* ThreadRoutine(void* arg)
{
    for(int i = 0; i < 10; ++i)
    {
        printf("[%d]Hello World\n", i + 1);
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, nullptr);
    for(int i = 0; i < 5; ++i)
    {
        sleep(1);
    }
    pthread_cancel(tid);
    pthread_join(tid, nullptr);
    cout << "main thread done" << endl;
    return 0;
}

运行结果:
在这里插入图片描述
在主线程取消该线程后, 该线程直接结束了.

5. pthread_detach

头文件: #include <pthread.h>
函数声明: int pthread_detach(pthread_t thread);

  • 成功返回 0, 失败返回 errno.
  • thread: 被分离的线程 id.

功能: 分离一个线程, 该线程不需要被等待(join)了.

示例代码:

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

void* ThreadRoutine(void* arg)
{
    for(int i = 0; i < 10; ++i)
    {
        printf("[%d]Hello World\n", i + 1);
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, nullptr);
    pthread_detach(tid);
    cout << "main thread done" << endl;
    return 0;
}

6. 示例代码

场景描述: 创建 10 个线程, 每个线程分别执行各自的累加任务, 执行完任务后结束, 最后主线程回收各线程, 程序结束.

代码:

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

//枚举退出状态
enum exit_code
{
    OK,
    ERROR
};


class thread_info
{
public:
    thread_info(string name, int top, int res = 0, int status = OK)
        :_name(name), _top(top)
    {}
public:
    //输入型参数
    string _name; //线程名
    int _top; //该线程要累加到的值

    //输出型参数
    int _res; //累加结果
    int _status; //退出码
};

//线程入口函数
void* Routine(void* arg)
{
    thread_info* ti = static_cast<thread_info*>(arg);
    cout << "线程" << pthread_self() << "计算中..." << endl;
    sleep(1);
    for(int i = 1; i <= ti->_top; ++i)
    {
        ti->_res += i;
    }
    cout << "线程" << pthread_self() << "计算完毕" << endl;
    sleep(1);
    pthread_exit(ti);
}

int main()
{
    pthread_t threads[10]; //各线程id
    for(int i = 0; i < 10; ++i)
    {
        char buf[64];
        snprintf(buf, sizeof(buf), "thread-%d", i);
        thread_info* ti = new thread_info(buf, 100 + i * 5);
        pthread_create(threads + i, NULL, Routine, ti);
    }

    void* arg;
    //回收线程
    for(int i = 0; i < 10; ++i)
    {
        pthread_join(threads[i], &arg);
        thread_info* res = static_cast<thread_info*>(arg);
        printf("线程%d已回收,其累加范围是[1,%d],累加结果:%d,退出码:%d\n", threads[i], res->_top, res->_res, res->_status);
        delete res;
    }

    cout << "线程回收完毕" << endl; 
    return 0;
}

运行结果:
在这里插入图片描述

程序运行起来同时运行的监控脚本:
在这里插入图片描述

从结果可以看出, 确实创建了 10 个线程来执行累加结果, 而最后也成功回收了各线程, 在创建线程时, 给入口函数传参不仅可以传递基础类型, 也可以传递复杂的结构类型, 代码中也是传递了一个自定义的类当作参数, 而从执行顺序可以得知, 线程的执行顺序是不确定的, 在监控脚本中可以看到运行的线程, 其中 PID 是整个进程的 PID, 因为一个进程内可以存在很多个执行流, 所以这些线程执行流的 PID 都是一样的, 而 LWP 则是每个线程都不同的.

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

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

相关文章

LangChain实战技巧之三:关于Tool的一点拓展

&#xff08;几乎&#xff09;任一LLM在bind_tools时&#xff0c;都是习惯先定义一个Function或BaseTool&#xff0c;然后再bind&#xff08;bind_tools&#xff09;具体方式可参考我的这篇文章 AI菜鸟向前飞 — LangChain系列之十三 - 关于Tool的必知必会 但这里的tool未必需…

GC日志中的Metaspace

GC 日志中的 Metaspace used 20580K, capacity 21180K, committed 21248K, reserved 1067008K class space used 2594K, capacity 2752K, committed 2816K, reserved 1048576K 一、Metaspace介绍 知道jdk8之前有perm这一整块内存来存klass等信息&#xff0c;我们的参数里也…

Python 文件操作指南:使用 open 和 with open 实现高效读写

&#x1f340; 前言 博客地址&#xff1a; CSDN&#xff1a;https://blog.csdn.net/powerbiubiu &#x1f44b; 简介 本系列文章主要分享文件操作&#xff0c;了解如何使用 Python 进行文件的读写操作&#xff0c;介绍常见文件格式的读取和写入方法&#xff0c;包括TXT、 CS…

vue 表格表头展示不下,显示。。。;鼠标悬浮展示全部

vue 表格表头展示不下&#xff0c;显示。。。&#xff1b;鼠标悬浮展示全部 <templateslot-scope"scope"slot"header"><span:title"临时证券类型"style"white-space:nowrap">{{ 临时证券类型 }}</span></templa…

老年人培聊助手:温暖心灵的智能陪伴

在快节奏的现代生活中&#xff0c;老年人常常感到孤独和寂寞。为了给他们带来更多的陪伴和温暖&#xff0c;我们推出了全新的老年人培聊助手智能体。 这款培聊助手智能体不仅是一个智能聊天工具&#xff0c;更是老年人生活中的贴心伙伴。它拥有自然流畅的语言交互能力&#xf…

一文详解SaaS增长模式:PLG、MLG、SLG哪种更适合你?

在SaaS&#xff08;软件即服务&#xff09;的领域中&#xff0c;增长策略的选择对于企业的成功至关重要。其中&#xff0c;PLG&#xff08;产品驱动增长&#xff09;、MLG&#xff08;市场驱动增长&#xff09;和SLG&#xff08;销售驱动增长&#xff09;是三种常见的策略&…

COD20使命召唤20新赛季免费玩 COD20免费体验在哪下

使命召唤20&#xff08;COD20&#xff09;的免费周已经正式启动&#xff0c;这是一个为期一周的特别活动&#xff0c;为玩家们带来了前所未有的游戏体验。在这个特殊的周期里&#xff0c;多人模式和僵尸模式将向公众免费开放&#xff0c;玩家们可以尽情地探索和体验游戏的精彩内…

PUBG绝地求生卡在初始界面 登不上去 打不开游戏的解决办法

PUBG绝地求生卡在初始界面 登不上去 打不开游戏的解决办法 吃鸡热潮依旧绝地求生PUBG可是咱们玩家的心头好啊&#xff01;不过有时候可能会遇到点小麻烦&#xff0c;比如PUBG绝地求生卡在初始界面 登不上去 打不开游戏的解决办法。小编这就给大家分享几个超实用的解决方法&…

想用Python做OCR?看看这篇文章

OCR是Optical Character Recognition的缩写&#xff0c;中文名为光学字符识别。它是一种通过计算机技术对图像或扫描文档中的文字进行识别和理解的过程。OCR技术可以将图像中的文字转换为可编辑、可搜索的文本&#xff0c;使得计算机能够理解和处理这些文字信息。 OCR技术通常…

马斯克拟打造xAI“算力超级工厂”,助力聊天机器人Grok

KlipC报道&#xff1a;马斯克计划推出xAI超级计算机&#xff0c;为下一代人工智能聊天机器人Grok提供动力&#xff0c;直言这将是一个“算力超级工厂”&#xff0c;并希望在2025年秋季之前能运行起来。 xAI是马斯克去年创立的人工智能初创公司&#xff0c;“尽可能寻求真相”、…

明日周刊-第11期

上周末去参加了软考&#xff0c;这个考试目前很热门&#xff0c;参加考试的人也非常多。笔者已经算是二战了&#xff0c;今年从笔试改革成了机考。618的购物活动也都已经开始了&#xff0c;我给狗子买了一袋进口的高端狗粮渴望&#xff0c;但是买回来发现它并不爱吃&#xff0c…

第五天 从零开始构建基于Kubernetes的DevOps平台

基于Kubernetes的DevOps平台实践 持续集成工具&#xff1a; JenkinsgitlabciTekton 本章基于k8s集群部署gitlab、sonarQube、Jenkins等工具&#xff0c;并把上述工具集成到Jenkins中&#xff0c;以Django项目和SpringBoot项目为例&#xff0c;通过多分支流水线及Jenkinsfile…

使用VUE3+TS+elementplus创建一个增加按钮

一、前言 在上一篇文章中分享了创建table的过程&#xff0c;详见&#xff08;VUE3TSelementplus创建table&#xff0c;纯前端的table&#xff09;&#xff0c;本文在创建好的table的基础上&#xff0c;再创建一个增加按钮。 二、程序展示 1、前面创建table的程序 <templ…

记一次攻防演练中的若依(thymeleaf 模板注入)getshell

记一次攻防演练中幸运的从若依弱口令到后台getshell的过程和分析。 0x01 漏洞发现 首先我会先把目标的二级域名拿去使用搜索引擎来搜索所用的搜索引擎收集到包含这个目标二级域名的三级域名或者四级域名的网站。 这样子可以快速的定位到你所要测试的漏洞资产。 1、推荐三个…

Raven2掠夺者2渡鸦2角色创建、游戏预下载、账号怎么注册教程

《渡鸦2》&#xff08;Raven 2&#xff09;是由韩国开发的一款大型多人在线角色扮演游戏&#xff08;MMORPG&#xff09;类型的手游&#xff0c;作为前作《Raven》的续集&#xff0c;继承并发展了其黑暗奇幻世界观&#xff0c;同时在游戏设计和内容上进行了大量创新。游戏预计于…

科技引领未来:高速公路可视化

高速公路可视化监控系统利用实时视频、传感器数据和大数据分析&#xff0c;通过图扑 HT 可视化展示交通流量、车速、事故和路况信息。交通管理人员可以实时监控、快速响应突发事件&#xff0c;并优化交通信号和指挥方案。这一系统不仅提高了道路安全性和车辆通行效率&#xff0…

阿里云天池AI课程证书学习计划

即日起&#xff0c;加入天池AI课程证书学习计划&#xff0c;解锁新技能&#xff0c;领取结业证书&#xff0c;我们的每一次学习、每一次成长都值得被见证&#xff01; https://tianchi.aliyun.com/specials/promotion/AICourses

【搭建大语言模型】使用LocalGPT搭建本地大语言模型服务并实现远程访问进行交互

文章目录 前言环境准备1. localGPT部署2. 启动和使用3. 安装cpolar 内网穿透4. 创建公网地址5. 公网地址访问6. 固定公网地址 前言 本文主要介绍如何本地部署LocalGPT并实现远程访问&#xff0c;由于localGPT只能通过本地局域网IP地址端口号的形式访问&#xff0c;实现远程访问…

[Python]pyenv 环境配置

。pip install pyenv安装 / 去Git 下载pyenv版本安装 。安装好后控制台输入pyenv查看版本 。pyenv install --list列出所有pyenv可以支持的python版本 。pyenv install 3.9.7 安装指定的版本 。在pycharm里&#xff0c;可以选中项目&#xff0c;点击File-Settings&#xff0…

python实现输入圆的半径,自动计算周长与面积

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、实现流程 1. 用户输入 2. 数据处理 3. 计算周长和面积 4. 结果展示 三、…