Linux C 线程编程:创建、结束、传参与常见问题解析
1. 引言在 Linux C 语言多线程编程中POSIX 线程pthread库提供了强大的线程管理能力。本文将系统性地介绍线程的创建、结束、线程 ID 获取以及线程间参数传递等核心操作并深入分析编程中常见的编译错误与运行时错误帮助开发者避开陷阱。2. 线程的创建与结束2.1 线程创建函数 pthread_create线程的创建通过pthread_create函数实现其原型如下#includepthread.hintpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);thread: 指向线程标识符TID的指针用于存储新线程的 ID。attr: 线程属性通常设为NULL使用默认属性。start_routine: 线程函数的入口地址该函数必须具有void* func(void*)的签名。arg: 传递给线程函数的参数类型为void*可以传递任意类型数据的地址。2.2 线程结束函数 pthread_exit线程可以通过以下三种方式结束线程函数执行return语句返回。线程调用pthread_exit函数主动退出。进程的main函数返回或调用exit导致所有线程终止。pthread_exit函数原型如下#includepthread.hvoidpthread_exit(void*retval);retval: 线程的退出状态值。这个值可以被其他线程通过pthread_join函数获取。调用pthread_exit会结束当前线程并释放其占用的私有资源如栈空间但不会影响其他线程。2.3 获取线程 ID 函数 pthread_self每个线程都有一个唯一的线程 IDTID。在线程内部可以通过pthread_self函数获取自己的 TID。#includepthread.hpthread_tpthread_self(void);该函数返回调用线程自身的线程标识符pthread_t类型常用于日志打印或线程间区分。3. 线程间参数传递详解pthread_create的最后一个参数arg是线程间传递数据的桥梁。由于其类型是void*它可以指向任何类型的数据但这也带来了类型安全的问题。3.1 基本传参方式传递整型值示例#includestdio.h#includepthread.hvoid*thread_func(void*arg){intvalue*(int*)arg;// 关键先类型转换再解引用printf(Received value: %d\n,value);returnNULL;}intmain(){pthread_ttid;intdata42;// 传递整型变量 data 的地址pthread_create(tid,NULL,thread_func,(void*)data);pthread_join(tid,NULL);return0;}3.2 重点与难点void* 指针的解引用这是初学者最容易出错的地方。void*是一种“无类型”指针编译器不知道它指向的数据类型和大小因此不能直接进行解引用操作*arg。常见编译错误createP_t.c:8:34: warning: dereferencing ‘void *’ pointer printf(input arg%d\n, (int)*arg); // 错误 createP_t.c:8:5: error: invalid use of void expression printf(input arg%d\n, (int)*arg);错误原因void *类型指针不能直接用*取值*arg因为编译器无法识别它指向的数据类型。正确解决方法必须先将其转换为目标类型的指针然后再解引用。// 假设 arg 指向一个 intintvalue*(int*)arg;// 正确先转换 (int*)再解引用 *3.3 参数传递的核心要点通过地址传递这是最常用的方式传递变量的地址data。在接收方必须进行正确的指针类型转换和解引用。值传递强制转换可以将一个整型值直接强制转换为void*进行传递。这种方式编译器会告警需要程序员自己保证数据长度正确且在线程函数中再转换回来。// 传递不推荐仅适用于小整数pthread_create(tid,NULL,thread_func,(void*)12345);// 接收longvalue(long)arg;传递结构体当需要传递多个参数时可以定义一个结构体传递其地址。structThreadArgs{intid;charname[20];};structThreadArgsargs{1,Thread-1};pthread_create(tid,NULL,thread_func,(void*)args);4. 常见运行时错误分析4.1 栈破坏错误 (Stack Smashing)错误信息*** stack smashing detected ***: ./mthread_t terminated 已放弃 (核心已转储)原因这通常是由于数组越界或缓冲区溢出导致的。在线程函数中如果你通过arg接收了一个指向局部数组栈内存的指针并在其他线程或主线程中修改了该数组且写入的数据超过了数组声明的长度就会破坏栈帧的保护机制Canary从而触发此错误。示例与避免void*bad_thread(void*arg){charbuffer[10];strcpy(buffer,(char*)arg);// 如果 arg 指向的字符串长度 9就会栈溢出// ...}避免方法确保写入缓冲区的数据不会超过其容量。对于字符串操作使用安全函数如strncpy并指定长度。考虑使用堆内存malloc来存储需要跨线程传递的大型或可变长度数据并记得在合适时机free。