getpid() vs pthread_self()<pthread.h>libpthread(编译时加-pthread)| 特性 | 线程 | 进程 |
|---|---|---|
| 创建开销 | 小(~10μs) | 大(~100μs) |
| 切换开销 | 小(~1μs) | 大(~10μs) |
| 通信方式 | 共享内存(直接) | IPC(管道/共享内存等) |
| 资源占用 | 共享地址空间 | 独立地址空间 |
| 安全性 | 低(数据竞争风险) | 高(天然隔离) |
| 典型应用 | 任务并行、IO重叠 | 服务隔离、安全边界 |
int pthread_create(
pthread_t *thread, // 输出:线程ID
const pthread_attr_t *attr, // 属性(NULL为默认)
void *(*start_routine)(void*), // 线程函数
void *arg // 传递给线程函数的参数
);
// 返回:成功返回0,失败返回错误码(不是-1)
int pthread_join(
pthread_t thread, // 要等待的线程ID
void **retval // 输出:线程函数的返回值
);
// 返回:成功返回0
// 阻塞直到目标线程结束
int pthread_detach(pthread_t thread);
// 分离后线程结束后自动回收资源,不能用pthread_join
// 常用:pthread_detach(pthread_self()) 在线程函数内部自我分离
void pthread_exit(void *retval);
// 终止当前线程,retval作为返回值
// 注意:不要返回局部变量地址
void* thread_func(void* arg) {
// 类型转换使用实际类型
int *data = (int*)arg;
// ...
int *result = malloc(sizeof(int));
*result = 42;
return (void*)result; // 返回堆内存或静态变量
}
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* thread_func(void* arg) {
int id = *(int*)arg;
printf("线程 %d 运行中, TID: %lu\n", id, pthread_self());
sleep(1);
int *result = malloc(sizeof(int));
*result = id * 100;
return (void*)result;
}
int main() {
pthread_t t1, t2;
int id1 = 1, id2 = 2;
void *ret1, *ret2;
if (pthread_create(&t1, NULL, thread_func, &id1) != 0) {
perror("pthread_create");
return 1;
}
if (pthread_create(&t2, NULL, thread_func, &id2) != 0) {
perror("pthread_create");
return 1;
}
pthread_join(t1, &ret1);
pthread_join(t2, &ret2);
printf("线程1返回: %d\n", *(int*)ret1);
printf("线程2返回: %d\n", *(int*)ret2);
free(ret1);
free(ret2);
return 0;
}
编译命令:
gcc -o pthread_basic pthread_basic.c -pthread
./pthread_basic
#include <stdio.h>
#include <pthread.h>
void* detached_thread(void* arg) {
printf("分离线程运行中\n");
sleep(2);
printf("分离线程结束\n");
pthread_detach(pthread_self()); // 自我分离
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, detached_thread, NULL);
// 主线程不需要pthread_join
sleep(1);
printf("主线程结束\n");
return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// UI线程(主线程)
void ui_task() {
for (int i = 0; i < 5; i++) {
printf("[UI] 刷新界面 %d\n", i);
usleep(500000);
}
}
// 后台数据处理线程
void* data_task(void* arg) {
int sensor_id = *(int*)arg;
for (int i = 0; i < 10; i++) {
printf("[DATA] 读取传感器%d: %d\n", sensor_id, i);
usleep(200000);
}
return NULL;
}
int main() {
pthread_t data_tid;
int sensor = 1;
// 启动后台数据采集线程
pthread_create(&data_tid, NULL, data_task, &sensor);
// 主线程处理UI
ui_task();
pthread_join(data_tid, NULL);
printf("所有任务完成\n");
return 0;
}
// 错误:返回局部变量地址(栈内存已释放)
void* bad_thread(void* arg) {
int result = 42;
return (void*)&result; // 未定义行为!
}
// 正确:使用堆内存或静态变量
void* good_thread(void* arg) {
int *result = malloc(sizeof(int));
*result = 42;
return (void*)result; // OK
}
// 错误:传递局部变量地址(主线程可能已修改)
int main() {
int val = 100;
pthread_create(&tid, NULL, thread_func, &val);
val = 200; // 可能在线程读取前被修改
}
// 正确:动态分配或确保生命周期
int *param = malloc(sizeof(int));
*param = 100;
pthread_create(&tid, NULL, thread_func, param);
| 错误码 | 含义 | 常见原因 |
|---|---|---|
| EAGAIN | 资源不足 | 线程数达到上限 |
| EINVAL | 无效参数 | 属性设置错误 |
| ESRCH | 找不到线程 | 线程已退出 |
| EDEADLK | 死锁 | 自己等待自己 |
-pthread编译确保链接pthread_cancel慎用,可能导致资源泄漏Q1:线程和进程的区别?为什么多线程比多进程更快?
进程是资源分配单位,线程是调度单位。同进程线程共享地址空间,创建/切换无需拷贝页表、刷新TLB,因此开销小10-100倍。通信也更简单,直接读写共享内存。
_*Q2:pthreadcreate的arg参数为什么是void*?如何安全传递复杂数据?*
void*是C的泛型机制,允许传递任意类型。安全做法:传递指针指向堆内存或结构体,确保生命周期覆盖线程执行期。避免传递栈上的局部变量地址。
Q3:pthread_join和pthread_detach的区别?什么时候用哪个?
join:主线程阻塞等待子线程结束,获取返回值,资源手动回收。 detach:子线程结束后自动回收,不能join。 选择:需要结果或控制用join;fire-and-forget任务用detach。
Q4:如果线程函数访问全局变量,会有什么问题?如何解决?
多线程并发访问共享变量导致数据竞争(race condition),结果不可预测。解决:使用互斥锁(pthread_mutex)、读写锁、原子操作或线程局部存储(__thread)。
Q5:嵌入式系统中多线程的典型应用场景?
- UI线程 + 数据采集线程(实时性)
- 网络通信线程 + 业务处理线程
- 多核并行计算(矩阵运算等)
- Watchdog线程监控主程序