Linux:线程创建与终止上(线程五)

一、核心结论

Linux 线程控制依赖 POSIX 线程库(pthread 库),核心操作包括创建、终止、等待、分离。线程创建通过pthread_create实现,终止有三种合法方式(return、pthread_exitpthread_cancel),需避免使用exit(终止整个进程)。

二、POSIX 线程库(pthread 库)基础

使用 pthread 库的核心注意事项:

  1. 头文件:#include <pthread.h>
  2. 编译链接:必须添加-lpthread参数(告知编译器链接线程库);
  3. 错误处理:pthread 函数不设置全局errno,错误码通过返回值返回,可通过strerror(ret)打印错误信息;
  4. 线程 ID:pthread_t类型,Linux 下本质是虚拟地址空间中的地址,进程内唯一。

三、线程创建:pthread_create 函数详解

1. 函数原型

复制代码
int pthread_create(pthread_t *restrict tidp, 
                   const pthread_attr_t *restrict attr,
                   void *(*start_routine)(void*), 
                   void *restrict arg);

2. 参数说明

参数名 作用
tidp 输出参数,存储新线程的 ID(用户态标识)
attr 线程属性(如栈大小、调度优先级),NULL 表示使用默认属性
start_routine 线程执行函数指针,原型为void* (*)(void*),参数和返回值均为void*
arg 传递给线程执行函数的参数,需强制类型转换为void*

3. 返回值

  • 成功:返回 0;
  • 失败:返回非 0 错误码(如 EAGAIN:资源不足,EPERM:无权限设置线程属性)。

4. 代码示例:创建简单线程

复制代码
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

// 线程执行函数
void* thread_routine(void* arg) {
    char* thread_name = (char*)arg;
    printf("线程%s启动,线程ID:%lu\n", thread_name, (unsigned long)pthread_self());
    sleep(2); // 模拟任务执行
    printf("线程%s执行完毕\n", thread_name);
    return (void*)100; // 线程返回值,后续可通过pthread_join获取
}

int main() {
    pthread_t tid;
    int ret;

    // 创建线程:默认属性,传递参数"线程1"
    ret = pthread_create(&tid, NULL, thread_routine, (void*)"线程1");
    if (ret != 0) {
        printf("创建线程失败:%s\n", strerror(ret));
        return -1;
    }

    printf("主线程ID:%lu,创建的线程ID:%lu\n", (unsigned long)pthread_self(), (unsigned long)tid);

    // 等待线程结束(后续章节详解)
    void* exit_status;
    ret = pthread_join(tid, &exit_status);
    if (ret != 0) {
        printf("等待线程失败:%s\n", strerror(ret));
        return -1;
    }

    printf("线程退出状态:%d\n", (int)exit_status);
    return 0;
}

5. 编译与运行

复制代码
gcc -o thread_create thread_create.c -lpthread
./thread_create

6. 运行结果

复制代码
主线程ID:140709324857152,创建的线程ID:140709316464384
线程1启动,线程ID:140709316464384
线程1执行完毕
线程退出状态:100

关键注意点:

  • 传递给线程的参数需是有效地址(全局变量、堆内存或数组),避免传递局部变量(主线程退出后地址失效);
  • pthread_self()获取的是用户态线程 ID,内核线程 ID 需通过syscall(SYS_gettid)获取(需包含<sys/syscall.h>

下面再补一份代码

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

class task
{
public:
    task(int a, int b)
        : _a(a)
        , _b(b)
    {}

    ~task()
    {}

    int Add()
    {
        return _a + _b;
    }

private:
    int _a;
    int _b;
};

class result
{
public:
    result(int result)
        : _result(result)
    {}

    ~result()
    {}

    int GetResult()
    {
        return _result;
    }

private:
    int _result;
};

/*
void* routine(void* args)
{
    task* t = (task*)args;
    sleep(100);
    result* r = new result(t->Add());
    sleep(1);
    //return (void*)r;
    pthread_exit(r); //结束新线程
    std::cout << "新线程不应该看到这里" << std::endl;
}
*/

void* routine(void* args)
{
    std::string name = (const char*)args;
    int cnt = 5;
    while (cnt--)
    {
        std::cout << "我是new线程, 我的name : " << name << " , cnt : " << cnt << std::endl;
        sleep(1);       
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, routine, (void*)"pthread -> 1");
    pthread_detach(tid);
    std::cout << "main线程将new线程分离" << std::endl;
    int cnt = 3;
    while (cnt--)
    {
        std::cout << "我是main线程" << std::endl;
        sleep(1);
    }
    int n = pthread_join(tid, nullptr);
    if (n == 0)
    {
        std::cout << "stay success" << std::endl;
    }
    else 
    {
        std::cout << "stay error : " << strerror(n) << std::endl;
    }
    return 0;
}

/*
int main()
{
    pthread_t tid;
    task* t = new task(1, 2);
    pthread_create(&tid, nullptr, routine, t);
    void* ret = nullptr;
    sleep(1);
    std::cout << "主线程将新线程终止" << std::endl;
    sleep(1);
    pthread_cancel(tid);
    pthread_join(tid, &ret);
    //std::cout << ((result*)ret)->GetResult() << std::endl;
    std::cout << "新线程终止返回值 : " << (long long)ret << std::endl;
    delete t;
    //delete (result*)ret;
    return 0;
}
*/
相关推荐
嵌入小生0072 小时前
双向链表、双向循环链表之间的异同---嵌入式入门---Linux
linux·c语言·数据结构·链表·嵌入式·小白
H Journey2 小时前
Linux sudo 命令完全指南
linux·运维·服务器·sudo
m0_748233172 小时前
PHP版本演进:从7.x到8.x全解析
java·开发语言·php
qq_12498707532 小时前
基于springboot的林业资源管理系统设计与实现(源码+论文+部署+安装)
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
开开心心_Every2 小时前
家常菜谱软件推荐:分类齐全无广告步骤详细
linux·运维·服务器·华为od·edge·pdf·华为云
i建模2 小时前
在 Arch Linux 中安装 **Xorg 服务器**
linux·运维·服务器
当战神遇到编程2 小时前
图书管理系统
java·开发语言·单例模式
liyuanchao_blog2 小时前
linuxptp适配记录
linux·云计算
indexsunny2 小时前
互联网大厂Java求职面试实战:Spring Boot微服务与Kafka消息队列应用解析
java·数据库·spring boot·微服务·面试·kafka·jpa