【Linux】线程操作

文章目录

  • 前言
  • 一、线程相关操作函数
    • 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 则是每个线程都不同的.

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

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

相关文章

python web自动化(Pytest实战)

1.UnitTest框架与Pytest框架对⽐ 1&#xff09; unittest框架介绍 Unittest则是Python语⾔的标准单元测试框架。 Unittest⽀持⾃动化测试&#xff0c;测试⽤例的初 始化、关闭和测试⽤例的聚合等功能&#xff0c;它有⼀个很重要的特性&#xff…

angr使用学习(持续更新)

首先我是直接在kali中安装的&#xff0c;也是边练边学的。 嗯&#xff0c;要在纯净python环境&#xff0c;所以是在 virtualenv 虚拟环境里&#xff0c;也不是特别会用这个&#xff0c;按照教程一步步做的 source venv/bin/activate 进入了对应环境 退出是 deactivate en,ipy…

中断处理过程介绍

概念 中断 中断源 分类 中断处理过程 中断请求 实现器件 中断判优 软件判优 过程 器件实现 程序实现 硬件判优 链路判优 器件实现 控制器判优 中断响应 中断服务 中断返回

RPA机器人流程自动化如何优化人力资源工作流程

人力资源部门在支持员工和改善整体工作环节方面扮演着至关重要的角色&#xff0c;但是在人资管理的日常工作中&#xff0c;充斥着大量基于规则的重复性任务&#xff0c;例如简历筛选、面试安排、员工数据管理、培训管理、绩效管理等&#xff0c;这些任务通常需要工作人员花费大…

以太坊钱包

以太坊钱包是你通往以太坊系统的门户。它拥有你的密钥&#xff0c;并且可以代表你创建和广播交易。选择一个以太坊钱包可能很困难&#xff0c;因为有很多不同功能和设计选择。有些更适合初学者&#xff0c;有些更适合专家。即使你现在选择一个你喜欢的&#xff0c;你可能会决定…

App Inventor 2 Encrypt.Security 安全性扩展:MD5哈希,SHA/AES/RSA/BASE64

这是关于App Inventor和Thunkable安全性的扩展&#xff0c;它提供MD5哈希&#xff0c;SHA1和SHA256哈希&#xff0c;AES加密/解密&#xff0c;RSA加密/解密&#xff0c;BASE64编码/解码方法。 权限 此扩展程序不需要任何权限。 事件 OnErrorOccured 抛出任何异常时将触发此事件…

【AN】制作二维机械专业的动画课件,播放、停止、显示和拖拽

下面代码实现了播放、停止、显示结构图&#xff0c;需要配合舞台的美术资源、元件动画来实现 stop(); Bt_play.addEventListener(MouseEvent.CLICK, playmc); function playmc(event:MouseEvent):void {MC_LINE01.play();MC_Jiantou.play();MC_xiexian.play();MC_LINE02.play()…

数据结构算法-堆(Heap)和优先队列

堆的概念 堆&#xff08;heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质&#xff1a; always greater than its child node/s and the key of the root node is the largest among all other nodes. This property…

企业选择定制化MES管理系统时需要考虑的核心功能

在当今制造业的数字化转型浪潮中&#xff0c;企业对于实现生产现场透明管理的需求愈发迫切。为了满足这一需求&#xff0c;MES管理系统成为了众多企业的首选解决方案。MES管理系统以其高度的灵活性和可定制性&#xff0c;能够根据不同行业的特性&#xff0c;为企业提供量身定制…

super关键字的使用

就是说我们有个子类和父类。子类继承父类。父类的私有的都不能访问&#xff0c;其他的都能够通过super访问&#xff0c;那么&#xff0c; 那么查找怎么查找呢&#xff0c;分两种情况&#xff0c; 1 子类和父类的属性没重名 直接用属性如n1, 2子类和父类有重名的 用super(…

大事件项目实战

初始化 创建项目 新建api_server文件夹为项目根目录&#xff0c;并在项目中运行如下的命令&#xff0c;初始化管理配置文件&#xff1a; npm init -y 运行如下的命令&#xff0c;安装特定版本的express: npm i express4.17.1 在项目根目录中新建app.js作为整个项目的入口…

深入探索C++模板进阶:掌握非类型参数、特化技巧与分离编译的艺术

目录 非类型模板参数 类模板的特化 概念 函数模板特化 类模板特化 全特化 偏特化 类模板特化应用示例 模板的分离编译 分离编译概念 模板的分离编译 解决方法 模板总结 非类型模板参数 模板参数可分为类型形参和非类型形参。 类型形参&#xff1a; 出现在模板参数列表中&am…

韬光养晦的超绝项目

发展方向 竞技闯关类 可以加入对战系统积累积分&#xff0c;竞技类的接受程度更高&#xff0c;小孩&#xff08;我和我身边大多数人小时候&#xff09;都喜欢玩王者吃鸡这种经济类游戏&#xff0c;开放世界探索&#xff08;本项目、一梦江湖、逆水寒&#xff09;的受众群体年…

8. C++通过epoll+fork的方式实现高性能网络服务器

epollfork 实现高性能网络服务器 一般在服务器上&#xff0c;CPU是多核的&#xff0c;上述epoll实现方式只使用了其中的一个核&#xff0c;造成了资源的大量浪费。因此我们可以将epoll和fork结合来实现更高性能的网络服务器。 创建子进程函数–fork( ) 要了解线程我们先来了解…

Linux下Qt Creator无法输入中文(已解决)

1. 首先确保安装了搜狗输入法&#xff0c;且能正常运行。 2.克隆源码到本地。 git clone https://gitcode.com/fcitx/fcitx-qt5.git 3.检查Qt Creator版本&#xff0c;如下图所示&#xff0c;为基于Qt6的。 4. 进入源码目录&#xff0c;建立build文件夹&#xff0c;修改CMak…

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

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

2022全国大学生数学建模竞赛ABC题(论文+代码)

文章目录 &#xff08;1&#xff09;2022A波浪能最大输出功率&#xff08;2&#xff09;2022B无人机定位&#xff08;3&#xff09;2022C古代玻璃制品成分分析&#xff08;4&#xff09;论文和代码链接 &#xff08;1&#xff09;2022A波浪能最大输出功率 &#xff08;2&#x…

3D开发工具HOOPS在BIM系统中的应用

建筑信息模型是一种革命性的建筑设计、施工和管理方法。它通过创建和利用数字信息来优化建筑项目的设计、施工和运营过程。在这个过程中&#xff0c;3D开发工具HOOPS扮演着至关重要的角色&#xff0c;为BIM系统提供了强大的技术支持和丰富的功能。HOOPS中文网http://techsoft3d…

Linux如何在目录下灵活创建、浏览、删除百万个文件

文章目录 一、创建百万级小文件1、单核CPU情况2、多核CPU情况3、执行效率对比3.1、单核的顺序执行3.2、多核的并发执行 二、如何列出/浏览这些文件1、查看目录下文件的数量2、列出&#xff1f;3、ls -f&#xff08;关闭排序功能&#xff09;3.1、执行效率对比 4、通过重定向导入…

探索Python函数参数的奥秘

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、揭开函数参数的神秘面纱 1. 位置参数&#xff1a;按序传值的基石 2. 关键字参数&#…