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