Linux 线程的创建

1. 引言

在操作系统中,进程(Process) 是资源分配的基本单位,但进程切换时涉及内存空间、文件描述符、寄存器等大量上下文保存与恢复,系统开销较大 。为了提升并发性能,现代操作系统引入了轻量级进程(Lightweight Process, LWP) ,也就是我们常说的线程(Thread)。

线程 是进程内的一个执行流,同一进程内的所有线程共享相同的地址空间、文件描述符、用户/组 ID 等资源,因此线程间的切换开销远小于进程切换。在 Linux 中,内核并不严格区分进程与线程,线程本质上就是共享大部分资源的进程。

2. 线程共享资源

一个进程中的多个线程共享以下资源:

  • 可执行的指令(代码段)
  • 静态数据(全局变量、静态变量)
  • 进程中打开的文件描述符
  • 当前工作目录
  • 用户 ID 与用户组 ID

这些共享资源使得线程间通信比进程间通信(IPC)更加高效,但也带来了同步与互斥的需求,以避免数据竞争。

3. 线程私有资源

每个线程拥有自己独立的执行上下文,私有资源包括:

  • 线程 ID(TID):唯一标识一个线程
  • 程序计数器(PC)及相关寄存器:记录当前执行位置
  • 堆栈:用于存放局部变量、函数调用链等
  • 错误号(errno):线程独立的错误状态
  • 优先级:调度优先级
  • 执行状态和属性:如是否可分离、栈大小等

4. Linux 线程库:pthread

Linux 系统下最常用的线程库是 POSIX 线程库(pthread),它提供了一套完整的线程操作 API:

  • 线程生命周期管理
    • 创建线程(pthread_create)
    • 回收线程(pthread_join)
    • 结束线程(pthread_exit、pthread_cancel)
  • 同步与互斥机制
    • 互斥锁(pthread_mutex_t)
    • 条件变量(pthread_cond_t)
    • 信号量(sem_t,需链接 -lpthread 或 -pthread)

5. 线程创建:pthread_create 详解

5.1 函数原型

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

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

5.2 参数说明

  • thread :指向 pthread_t 类型变量的指针,用于存储新线程的标识符。
  • attr :线程属性指针,可设置栈大小、调度策略等;传入 NULL 表示使用默认属性。
  • routine :线程入口函数,其类型必须为 void *(*)(void *),即接收一个 void* 参数并返回 void*。
  • arg :传递给 routine 的参数,类型为 void*,可传递任意类型数据的地址。

5.3 返回值

  • 成功时返回 0。
  • 失败时返回错误码 (非负整数),可通过 strerror 输出错误信息。

5.4 获取当前线程 ID

c 复制代码
pthread_t pthread_self(void);

该函数返回调用线程自身的线程 ID(TID)。

注意 :主进程退出时,它创建的所有线程也会随之退出。此外,线程创建需要一定的时间,如果主进程在创建线程后立即退出,新创建的线程可能来不及执行。因此,在实际编程中,通常需要让主进程等待线程完成(例如使用 pthread_join)或通过其他同步机制确保线程有机会执行。

6. 常见编译与链接错误

6.1 错误一:线程函数类型不匹配

报错信息示例(编译时):

复制代码
error: incompatible type for argument 3 of 'pthread_create'
expected 'void * (*)(void *)', but argument is of type 'int * (*)(char *)'

原因 :pthread_create 的第三个参数要求函数签名严格为 void* (*)(void*),如果自定义的线程函数返回类型或参数类型不匹配,就会报此错误。

解决方法:将线程函数强制转换为正确的类型。

c 复制代码
// 假设原函数为 int* testThread(char* arg);
pthread_create(&tid, NULL, (void* (*)(void*))testThread, NULL);

6.2 错误二:未定义引用 pthread_create(链接错误)

报错信息示例(链接时):

复制代码
createP_t.c:(.text+0x4b):对'pthread_create'未定义的引用
collect2: error: ld returned 1 exit status

原因 :编译器找到了 pthread_create 的声明(头文件 pthread.h),但链接时找不到其实现。这是因为 pthread 库是系统库,需要显式链接。

解决方法 :在编译命令末尾添加 -lpthread 或 -pthread 选项。

bash 复制代码
gcc -o my_program my_program.c -lpthread
# 或
gcc -o my_program my_program.c -pthread

-pthread 选项除了链接库外,还会定义必要的宏,推荐使用。

7. 完整示例:创建并运行一个线程

下面是一个简单的示例,演示如何创建线程并传递参数。

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

// 线程入口函数
void* thread_func(void* arg) {
    int* p_num = (int*)arg;
    printf("Thread started, received number: %d\n", *p_num);
    sleep(1);
    printf("Thread finished.\n");
    return NULL;
}

int main() {
    pthread_t tid;
    int num = 42;

    // 创建线程
    int ret = pthread_create(&tid, NULL, thread_func, &num);
    if (ret != 0) {
        fprintf(stderr, "Failed to create thread: %s\n", strerror(ret));
        return 1;
    }

    printf("Main thread: created thread with ID %lu\n", (unsigned long)tid);

    // 等待线程结束(线程回收)
    pthread_join(tid, NULL);

    printf("Main thread: all threads completed.\n");
    return 0;
}

编译与运行:

bash 复制代码
gcc -o thread_demo thread_demo.c -lpthread
./thread_demo
相关推荐
en.en..几秒前
Linux 进程退出exit(函数)
java·开发语言·算法
Cx330❀3 分钟前
【Qt 进阶指南】详解 Qt 常用标准对话框与主窗口组件架构
开发语言·qt·ui·性能优化·架构·图形渲染
Light Gao4 分钟前
Encoder 与 Decoder 详解:从 Embedding 到 Seq2Seq 的完整数据流
java·开发语言·embedding
HZZD_HZZD5 分钟前
弱网下CoAP还省吗?合众致达Cat.1电表90天实测:MQTT重连流量占52%、续航差距从38%拉大到73%
开发语言·物联网·php·腾讯云
web打印社区8 分钟前
Windows 网页静默打印设置步骤:客户端、防火墙与联调清单
开发语言·前端·javascript·chrome·pdf·ecmascript
云栖梦泽12 分钟前
Linux内存管理
linux·运维·服务器
小师兄吃牛肉17 分钟前
什么是R语言?如何快速学习R语言
开发语言·学习·r语言
Lucis__28 分钟前
编译链接全链路解析:ELF形成到可执行加载的发展历程
linux·程序·elf文件
phltxy38 分钟前
C 语言文件操作:让数据从内存走向持久化
c语言·网络·数据库
Evand J39 分钟前
【MATLAB例程】三维快速扩展随机树(RRT)路径规划与到达角(AOA)、到达时间差(TDOA)融合定位,附下载链接
开发语言·matlab·代码·定位·例程