c多线程编程实例 pthread创建同步与编译运行
时间:2026-08-10 | 作者:清风无痕 | 阅读:0本文通过一个基于pthread的完整C语言实例,说明线程创建、参数传递、等待回收和互斥锁同步的基本方法,并分析共享数据竞争等常见问题,帮助读者掌握多线程程序从编写、编译到安全运行的完整过程。
实例目标与运行环境
下面以POSIX线程库为基础设计一个计数实例。主线程创建两个工作线程,每个线程循环增加同一个共享计数器。由于自增操作并非不可分割,两个线程同时读写计数器时可能覆盖彼此的结果,因此需要使用互斥锁保护临界区。该实例适用于支持pthread的Linux及其他类Unix环境。
- 引入
pthread.h头文件,使用pthread_t保存线程标识。 - 使用共享计数器演示多个线程访问同一数据的场景。
- 使用
pthread_mutex_t互斥锁保证每次自增操作完整执行。
创建线程与传递参数
线程入口函数通常接收一个void指针并返回一个void指针。可以定义参数结构体保存线程编号和循环次数,再把对应结构体的地址传给pthread_create。参数对象在线程结束前必须保持有效,不能传入已经离开作用域的局部对象地址。创建成功时pthread_create返回零,失败时应保存返回码并输出错误信息,同时避免等待未创建的线程。
- 分别准备两个参数结构体,避免工作线程错误地共用同一份可变参数。
- 调用
pthread_create时传入线程标识地址、属性、入口函数和参数地址。 - 在线程函数中先把void指针转换为正确的结构体指针,再读取参数。
完整可编译的计数实例
下面的source.c包含参数结构体、线程入口函数、共享计数器、互斥锁、两个线程的创建状态、条件化pthread_join以及互斥锁销毁。两个线程各执行100000次加一操作。即使某次pthread_create失败,程序也只会等待实际创建成功的线程,并通过strerror把pthread返回码转换为明确的错误信息。
#include
#include
#include
#include
#include
typedef struct {
int thread_id;
long iterations;
} ThreadArgs;
static long shared_counter = 0;
static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
static void *count_worker(void *arg)
{
const ThreadArgs *args = (const ThreadArgs *)arg;
for (long i = 0; i < args->iterations; ++i) {
int rc = pthread_mutex_lock(&counter_mutex);
if (rc != 0) {
fprintf(stderr, "thread %d: pthread_mutex_lock: %sn",
args->thread_id, strerror(rc));
return (void *)(long)rc;
}
++shared_counter;
rc = pthread_mutex_unlock(&counter_mutex);
if (rc != 0) {
fprintf(stderr, "thread %d: pthread_mutex_unlock: %sn",
args->thread_id, strerror(rc));
return (void *)(long)rc;
}
}
return NULL;
}
int main(void)
{
pthread_t threads[2];
ThreadArgs args[2] = {
{ .thread_id = 1, .iterations = 100000 },
{ .thread_id = 2, .iterations = 100000 }
};
int create_rc[2];
int program_failed = 0;
for (int i = 0; i < 2; ++i) {
create_rc[i] = pthread_create(&threads[i], NULL, count_worker, &args[i]);
if (create_rc[i] != 0) {
fprintf(stderr, "pthread_create for thread %d: %sn",
args[i].thread_id, strerror(create_rc[i]));
program_failed = 1;
}
}
for (int i = 0; i < 2; ++i) {
if (create_rc[i] == 0) {
void *thread_result = NULL;
int join_rc = pthread_join(threads[i], &thread_result);
if (join_rc != 0) {
fprintf(stderr, "pthread_join for thread %d: %sn",
args[i].thread_id, strerror(join_rc));
program_failed = 1;
} else if (thread_result != NULL) {
fprintf(stderr, "thread %d exited with error code %ldn",
args[i].thread_id, (long)thread_result);
program_failed = 1;
}
}
}
printf("final counter: %ldn", shared_counter);
int destroy_rc = pthread_mutex_destroy(&counter_mutex);
if (destroy_rc != 0) {
fprintf(stderr, "pthread_mutex_destroy: %sn", strerror(destroy_rc));
program_failed = 1;
}
return program_failed EXIT_FAILURE : EXIT_SUCCESS;
} - create_rc数组分别保存两次
pthread_create的返回值,只有返回值为0时才执行对应的pthread_join。 pthread_create、pthread_join和互斥锁操作的错误码均通过strerror转换为可读信息。- 所有已创建线程完成后才销毁互斥锁,参数数组在main结束前始终有效。
使用互斥锁保护共享计数器
每次修改共享计数器前调用pthread_mutex_lock,修改完成后调用pthread_mutex_unlock。锁定范围应只覆盖必须互斥的代码,耗时计算、文件读取或休眠等操作不宜放在临界区内,否则会降低并发效率。互斥锁可以使用PTHREAD_MUTEX_INITIALIZER静态初始化,也可以通过pthread_mutex_init动态初始化。无论采用哪种初始化方式,只要互斥锁仍处于有效状态且后续不再使用,都应在没有线程持有或等待该锁时调用pthread_mutex_destroy。
- 获得互斥锁后执行共享计数器自增。
- 离开临界区前必须释放互斥锁,所有分支都应遵守这一规则。
- 确认所有相关线程已经退出且无人持有或等待互斥锁后,再调用
pthread_mutex_destroy。
编译运行并检查结果
把完整代码保存为source.c,然后使用gcc source.c -o demo -pthread编译,运行命令为./demo。两个线程都成功创建并各完成十万次自增时,典型输出为final counter: 200000。程序也会检查pthread_join的返回值;如果线程创建、执行、等待或互斥锁销毁失败,将向标准错误输出具体原因并以失败状态退出。
- 编译命令:
gcc source.c -o demo -pthread - 运行命令:./demo
- 典型输出:final counter: 200000
常见错误与改进方向
多线程程序的问题往往具有偶发性,一次运行正确并不能证明没有数据竞争。可以暂时去掉count_worker中的pthread_mutex_lock和pthread_mutex_unlock进行对照测试:shared_counter的自增会变成未同步的并发读写,程序存在数据竞争,结果属于未定义行为,常见现象是最终值小于200000,也可能偶尔看似正确。不要把某一次输出当作无锁写法可靠的证据。忘记加锁、提前释放参数、多个线程写入同一缓冲区以及线程未回收,也会造成错误或资源泄漏。实际项目还应根据任务模型选择条件变量、读写锁或线程池,并明确线程之间的数据所有权和退出流程。
- 不要依赖运行速度或休眠时间维持线程执行顺序。
- 不要在持有互斥锁时执行无法确定耗时的操作。
- 共享状态较多时,应封装访问函数并统一管理加锁和解锁。
完成线程创建、参数传递、错误处理、同步和回收后,一个基础的C多线程程序就具备了可编译、可验证的可靠结构。继续扩展任务队列和条件变量,可以逐步实现更实用的并发处理程序。
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- c 语言基础教程怎么入门?语法、流程与示例说明
- 时间:2026-08-13
-
- c实现多线程 pthread创建回收与互斥锁同步
- 时间:2026-08-10
-
- c++11 正则表达式 std::regex写法与异常处理
- 时间:2026-08-10
-
- 多线程c语言实现 pthread创建同步与完整示例
- 时间:2026-08-10
-
- vc 正则表达式 std::regex与Boost.Regex选择
- 时间:2026-08-10
-
- c++ 正则表达 regex匹配提取替换示例
- 时间:2026-08-10
-
- c的多线程怎么实现原理 pthread原理与加锁示例
- 时间:2026-08-10
-
- 正则表达式c语言
- 时间:2026-08-10
精选合集
更多大家都在玩
大家都在看
更多-
- 糖尿病完全不能吃糖吗
- 时间:2026-09-15
-
- 蚂蚁庄园小课堂2026年9月16日最新题目答案
- 时间:2026-09-15
-
- 小鸡答题今天的答案是什么2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园每日答题答案2026年9月16日
- 时间:2026-09-15
-
- 以下哪种粮食是酿造绍兴黄酒的主要原料 蚂蚁庄园今日答案9月16日
- 时间:2026-09-15
-
- 劝学名句“及时当勉励,岁月不待人”出自哪位诗人 蚂蚁庄园今日答案9.16
- 时间:2026-09-15
-
- 蚂蚁庄园今天答题答案2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园答题今日答案2026年9月16日
- 时间:2026-09-15