Linux线程的操作

目录

[1 创建线程](#1 创建线程)

[1.1 pthread_create](#1.1 pthread_create)

[2.2 pthread_self](#2.2 pthread_self)

[2 线程等待](#2 线程等待)

[2.1 pthread_join](#2.1 pthread_join)

[3 线程终止](#3 线程终止)

[3.1 pthread_exit](#3.1 pthread_exit)

[3.2 pthread_cancel](#3.2 pthread_cancel)

[4 线程分离](#4 线程分离)

[4.1 pthread_detach](#4.1 pthread_detach)

[5 POSIX库](#5 POSIX库)

[5.1 线程ID及进程地址空间布局](#5.1 线程ID及进程地址空间布局)


1 创建线程


1.1 pthread_create

(1)功能

pthread_create 是 POSIX 线程(pthread)库中用于创建新线程的函数,它是 Linux 和其他类 Unix 系统中多线程编程的基础。

(2)函数原型

cpp 复制代码
#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

(3)函数参数

  • thread(输出型参数)
  1. 指向 pthread_t 类型的指针,用于存储新线程的标识符
  2. 线程创建成功后,系统会将线程ID写入这个位置
  • attr
  1. 指向线程属性对象的指针,用于设置线程属性(如栈大小、调度策略等)
  2. 如果为 NULL,则使用默认属性
  • start_routine
  1. 线程启动后执行的函数指针
  2. 函数形式应为:void *func_name(void *arg)
  • arg
  1. 传递给 start_routine 的参数
  2. 如果需要传递多个参数,可以封装在结构体中

(4)返回值

  • 成功:返回 0
  • 失败 :返回错误代码(不是设置 errno

(5)示例

cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <pthread.h>

// 线程执行入口
void* thread_func(void* args)
{
    std::string str = (char*)args;
    // 第二个循环
    while(true)
    {
        printf("Second thread:%s pid:%d num:%d\n", str.c_str(), getpid(), num);
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    // 创建线程
    pthread_create(&tid, nullptr, thread_func, (void*)"pthread-1");
    // 主线程
    // 第一个循环
    while(true)
    {
        printf("Main thread pid:%d num:%d\n", getpid(), num);
        sleep(1);
    }
    return 0;
}

2.2 pthread_self

(1)功能

pthread_self 是 POSIX 线程库中用于获取调用线程自身轻量级进程PID的函数

(2)函数原型

cpp 复制代码
#include <pthread.h>

pthread_t pthread_self(void);

(3)返回值

  • 返回调用线程的线程标识符(pthread_t 类型)

2 线程等待


如果主线程结束,就代表整个进程结束了,进程结束,所有线程全部退出,哪怕没有执行完,因此在多线程中,主线程应该是最后退出的

为什么需要线程等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复⽤刚才退出线程的地址空间

2.1 pthread_join

(1)功能

pthread_join 是 POSIX 线程(pthread)库中用于等待指定线程终止并回收其资源的函数

(2)函数原型

cpp 复制代码
#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

(3)函数参数

  • thread
  1. 要等待的线程标识符(由 pthread_create 返回的 pthread_t 类型)
  • retval
  1. 指向指针的指针,用于存储目标线程的返回值
  2. 如果不关心返回值,可以设置为 NULL
  3. 目标线程的返回值通过 pthread_exit()return 语句返回

(4)返回值

  • 成功:返回 0
  • 失败 :返回错误代码(不是设置 errno

(5)示例

cpp 复制代码
// 线程执行入口
void* thread_func(void* args)
{
    std::string str = (char*)args;

    int cnt = 5;
    // 第二个循环
    while(cnt--)
    {
        printf("New thread:%s\n", str.c_str());
        sleep(1);
    }

    // 返回一个整数
    return (void*)10;
}

int main()
{
    // 线程名字
    const char* name = "thread-1";
    
    pthread_t tid;
    // 创建线程
    int pid = pthread_create(&tid, nullptr, thread_func, (void*)name);
    
    // 主线程
    // 等待线程
    // 记录退出信息
    void* ret = nullptr;
    int n = pthread_join(tid, &ret);
    // 不需要考虑线程异常问题,因为没机会处理
    printf("wait 0x%p succeed, thread exit information:%lld\n", (void*)tid, (long long)ret);

    return 0; 
}

(6)应用层面传参和返回值

cpp 复制代码
// 模拟一个简单的线程任务类
class Task
{
public:
    Task(int x = 0, int y = 0, int result = 0, int exitCode = 0)
        :_x(x)
        ,_y(y)
        ,_result(result)
        ,_exitCode(exitCode)
    {}

    void Div()
    {
        if(_y == 0) 
        {
            // 出现错误,设置错误码
            _exitCode = 1;
            return;
        }
        _result = _x / _y;
    }

    void Print()
    {
        std::cout<<"result:"<<_result<<" exitCode:"<<_exitCode<<std::endl;
    }

private:
    int _x;
    int _y;
    int _result;
    int _exitCode;
};

// 线程执行入口
void* thread_func(void* args)
{
    // 接收任务类
    Task* task = (Task*)args;
    task->Div();

    // 返回任务类
    return (void*)task;
}

int main()
{
    // 线程名字
    const char* name = "thread-1";
    
    // 创建任务类
    Task* task = new Task(20, 0);
    pthread_t tid;
    // 创建线程,传递任务
    int pid = pthread_create(&tid, nullptr, thread_func, (void*)task);
    
    // 主线程
    // 等待线程
    // 记录退出信息
    void* ret = nullptr;
    int n = pthread_join(tid, &ret);

    task = (Task*)ret;
    task->Print();

    // 不需要考虑线程异常问题,因为没机会处理
    printf("wait 0x%p succeed, thread exit information:%lld\n", (void*)tid, (long long)ret);

    return 0; 
}

3 线程终止


如果需要只终⽌某个线程⽽不终⽌整个进程,可以有三种⽅法:

  1. 从线程函数return。(这种⽅法对主线程不适⽤,从main函数return相当于调⽤exit)
  2. 线程可以调⽤pthread_ exit终⽌⾃⼰。
  3. ⼀个线程可以调⽤pthread_ cancel终⽌同⼀进程中的另⼀个线程。

3.1 pthread_exit

(1)功能

pthread_exit 是 POSIX 线程库中用于显式终止调用线程的函数,它允许线程在结束执行时返回一个值给其他等待它的线程。

(2)函数原型

cpp 复制代码
#include <pthread.h>

void pthread_exit(void *retval);

(3)函数参数

  • retval
    • 线程的退出状态值,可以被其他线程通过 pthread_join 获取
    • 可以传递任何类型的指针,但接收方需要知道如何解析
    • 如果不需要返回值,可以设为 NULL

3.2 pthread_cancel

(1)功能

pthread_cancel 是 POSIX 线程库中用于请求取消(终止)另一个线程的函数。它提供了一种异步终止线程的机制。

(2)函数原型

cpp 复制代码
#include <pthread.h>

int pthread_cancel(pthread_t thread);

(3)函数参数

  • thread
    • 要取消的目标线程的标识符(由 pthread_create 返回的 pthread_t 类型)

(4)返回值

  • 成功:返回 0
  • 失败 :返回错误代码(不是设置 errno

4 线程分离


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

如果不关心线程的返回值,join是⼀种负担,这个时候,我们可以告诉系统,当线程退出时,⾃动释放线程资源

4.1 pthread_detach

(1)功能

pthread_detach 是 POSIX 线程库中用于将线程标记为"分离状态"(detached state)的函数,使线程终止时能够自动释放资源,无需其他线程调用 pthread_join。

(2)函数原型

cpp 复制代码
#include <pthread.h>

int pthread_detach(pthread_t thread);

(3)函数参数

  • thread
    • 要设置为分离状态的线程标识符(由 pthread_create 返回的 pthread_t 类型)

(4)返回值

  • 成功:返回 0
  • 失败 :返回错误代码(不是设置 errno

(5)示例

cpp 复制代码
// 线程执行入口
void* thread_func(void* args)
{
    // 线程主动分离
    pthread_detach(pthread_self());
    std::string str = (char*)args;
    // 返回
    return (void*)10;
}

int main()
{
    // 线程名字
    const char* name = "thread-1";

    pthread_t tid;
    // 创建线程,传递任务
    int pid = pthread_create(&tid, nullptr, thread_func, (void*)name);

    // 主进程分离指定线程
    pthread_detach(tid);
    sleep(1);
    
    // 主线程
    // 等待线程
    // 记录退出信息
    void* ret = nullptr;
    int n = pthread_join(tid, &ret);

    // 不需要考虑线程异常问题,因为没机会处理
    printf("wait 0x%p succeed, thread exit information:%lld, code:%d\n", (void*)tid, (long long)ret, n);

    return 0; 
}

5 POSIX库


与线程有关的函数构成了⼀个完整的系列,绝⼤多数函数的名字都是以"pthread_"打头的,要使⽤这些函数库,要通过引⼊头文件

链接这些线程函数库时要使⽤编译器命令的"-lpthread"选项因为头文件 不属于C标准库,属于Linux系统的原生库,也就是第三方库

5.1 线程ID及进程地址空间布局

pthread_ create函数会产⽣⼀个线程ID,存放在第⼀个参数指向的地址中。该线程ID和前⾯说的线程ID不是⼀回事。前⾯讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最⼩单位,所以需要⼀个数值来唯⼀表⽰该线程。

pthread_ create函数第⼀个参数指向⼀个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库(pthread库)的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。

问题:thread_t 到底是什么类型呢?

取决于实现。对于Linux⽬前实现的NPTL实现⽽⾔,pthread_t类型的线程ID,本质就是⼀个进程地址空间上的⼀个地址。线程库(pthread)中创建的TCB结构体的起始地址

相关推荐
曹牧2 小时前
Java:上传文件到网页
java·开发语言
lly2024062 小时前
CSS3 3D 转换
开发语言
softshow10262 小时前
在 Ubuntu 下进行磁盘分卷
linux·数据库·ubuntu
江沉晚呤时2 小时前
C# 高级多态揭秘:从虚函数表到性能优化实战
开发语言·c#·.net
lulu12165440782 小时前
谷歌Gemma 4实战指南:Apache 2.0开源,移动端AI新时代来临
java·开发语言·人工智能·开源·apache·ai编程
榴莲omega2 小时前
第11天:函数组合、记忆化与定时器
开发语言·前端·javascript
Deitymoon2 小时前
linux——共享内存
linux
DeepHacking2 小时前
Ubuntu上面加速下载文件
linux·运维·ubuntu
EAIReport2 小时前
深入浅出理解Token技术计算方式:从原理到实战
linux·运维·服务器