当前位置: 首页 > news >正文

[学习] C语言编程中线程安全的实现方法(示例)

🧵C语言编程中线程安全的实现方法

在多线程编程中,线程安全(Thread Safety) 是一个非常重要的概念。当多个线程同时访问共享资源时,如果没有合理的同步机制,就可能导致数据竞争、死锁甚至程序崩溃。

本文将详细介绍在 C语言 中如何实现线程安全的几种主要方式,并提供可以实际运行的代码示例。

文章目录

  • 🧵C语言编程中线程安全的实现方法
    • 🔹 一、什么是线程安全?
    • 🔹 二、C语言中线程安全的实现方式
    • 🔸 方法一:互斥锁(Mutex)
      • ✅ 示例:用互斥锁保护计数器
    • 🔸 方法二:读写锁(Read-Write Lock)
      • ✅ 示例:读写锁保护共享结构体
    • 🔸 方法三:条件变量(Condition Variable)
      • ✅ 示例:生产者-消费者模型
    • 🔸 方法四:原子操作(Atomic Operations)
      • ✅ 示例:使用原子变量实现计数器
    • 🔸 方法五:线程局部存储(TLS)
      • ✅ 示例:使用 `__thread` 关键字(GCC扩展)
    • 🔹 总结对比表
    • 🔹 小贴士
    • 🔁 结语


🔹 一、什么是线程安全?

线程安全是指:当多个线程并发访问某个函数或对象时,其行为仍然正确,不会因为并发访问而产生错误结果。


🔹 二、C语言中线程安全的实现方式

在C语言中,最常用的线程库是 POSIX Threads(pthread)。我们主要使用以下机制来实现线程安全:

  1. 互斥锁(Mutex)
  2. 读写锁(Read-Write Lock)
  3. 条件变量(Condition Variable)
  4. 原子操作(Atomic Operations)
  5. 线程局部存储(Thread Local Storage, TLS)

下面我们逐一介绍每种方法,并给出完整示例代码。


🔸 方法一:互斥锁(Mutex)

互斥锁是最基本也是最常用的同步机制。它可以保证同一时间只有一个线程能访问共享资源。

✅ 示例:用互斥锁保护计数器

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>int counter = 0;
pthread_mutex_t lock; // 定义互斥锁void* increment_counter(void* arg) {for (int i = 0; i < 100000; ++i) {pthread_mutex_lock(&lock); // 加锁counter++;pthread_mutex_unlock(&lock); // 解锁}return NULL;
}int main() {pthread_t t1, t2;// 初始化互斥锁pthread_mutex_init(&lock, NULL);// 创建两个线程pthread_create(&t1, NULL, increment_counter, NULL);pthread_create(&t2, NULL, increment_counter, NULL);// 等待线程结束pthread_join(t1, NULL);pthread_join(t2, NULL);// 销毁互斥锁pthread_mutex_destroy(&lock);printf("Final counter value: %d\n", counter); // 应为 200000return 0;
}

📌 编译命令:

gcc -o mutex_example mutex_example.c -lpthread

🔸 方法二:读写锁(Read-Write Lock)

适用于读多写少的场景。允许多个线程同时读取资源,但只允许一个线程写入。

✅ 示例:读写锁保护共享结构体

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>typedef struct {char data[128];
} SharedData;SharedData shared;
pthread_rwlock_t rwlock; // 定义读写锁void* reader(void* arg) {for (int i = 0; i < 5; ++i) {pthread_rwlock_rdlock(&rwlock);printf("Reader: %s\n", shared.data);pthread_rwlock_unlock(&rwlock);sleep(1);}return NULL;
}void* writer(void* arg) {const char* messages[] = {"Hello", "World", "Thread", "Safety", "Example"};for (int i = 0; i < 5; ++i) {pthread_rwlock_wrlock(&rwlock);strcpy(shared.data, messages[i]);printf("Writer: Updated to '%s'\n", shared.data);pthread_rwlock_unlock(&rwlock);sleep(2);}return NULL;
}int main() {pthread_t r1, r2, w1;// 初始化读写锁pthread_rwlock_init(&rwlock, NULL);// 初始化共享数据strcpy(shared.data, "Initial");// 创建线程pthread_create(&r1, NULL, reader, NULL);pthread_create(&r2, NULL, reader, NULL);pthread_create(&w1, NULL, writer, NULL);// 等待所有线程完成pthread_join(r1, NULL);pthread_join(r2, NULL);pthread_join(w1, NULL);// 销毁锁pthread_rwlock_destroy(&rwlock);return 0;
}

📌 编译命令:

gcc -o rwlock_example rwlock_example.c -lpthread

🔸 方法三:条件变量(Condition Variable)

用于线程间的通信,常与互斥锁配合使用,等待某个条件成立后再继续执行。

✅ 示例:生产者-消费者模型

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>#define MAX_ITEMS 5int buffer = -1;
int count = 0;
pthread_mutex_t mutex;
pthread_cond_t not_empty, not_full;void* producer(void* arg) {for (int i = 0; i < 10; ++i) {pthread_mutex_lock(&mutex);while (count == MAX_ITEMS) {pthread_cond_wait(&not_full, &mutex);}buffer = i;count++;printf("Produced: %d\n", i);pthread_cond_signal(&not_empty);pthread_mutex_unlock(&mutex);sleep(1);}return NULL;
}void* consumer(void* arg) {for (int i = 0; i < 10; ++i) {pthread_mutex_lock(&mutex);while (count == 0) {pthread_cond_wait(&not_empty, &mutex);}int item = buffer;count--;printf("Consumed: %d\n", item);pthread_cond_signal(&not_full);pthread_mutex_unlock(&mutex);sleep(2);}return NULL;
}int main() {pthread_t prod, cons;// 初始化锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&not_empty, NULL);pthread_cond_init(&not_full, NULL);// 创建线程pthread_create(&prod, NULL, producer, NULL);pthread_create(&cons, NULL, consumer, NULL);// 等待线程结束pthread_join(prod, NULL);pthread_join(cons, NULL);// 清理资源pthread_mutex_destroy(&mutex);pthread_cond_destroy(&not_empty);pthread_cond_destroy(&not_full);return 0;
}

📌 编译命令:

gcc -o condvar_example condvar_example.c -lpthread

🔸 方法四:原子操作(Atomic Operations)

适用于对某些变量进行不可中断的操作,如递增、比较交换等。

注意:C11标准引入了 _Atomic 类型和 <stdatomic.h> 头文件。

✅ 示例:使用原子变量实现计数器

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>_Atomic int atomic_counter = 0;void* inc_atomic(void* arg) {for (int i = 0; i < 100000; ++i) {atomic_fetch_add(&atomic_counter, 1);}return NULL;
}int main() {pthread_t t1, t2;pthread_create(&t1, NULL, inc_atomic, NULL);pthread_create(&t2, NULL, inc_atomic, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);printf("Atomic counter final value: %d\n", atomic_counter); // 应为 200000return 0;
}

📌 编译命令:

gcc -o atomic_example atomic_example.c -lpthread -std=c11

🔸 方法五:线程局部存储(TLS)

每个线程拥有独立的变量副本,避免共享带来的竞争问题。

✅ 示例:使用 __thread 关键字(GCC扩展)

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>__thread int tls_counter = 0; // 每个线程独立的计数器void* thread_func(void* arg) {for (int i = 0; i < 5; ++i) {tls_counter++;printf("Thread %ld: tls_counter = %d\n", pthread_self(), tls_counter);sleep(1);}return NULL;
}int main() {pthread_t t1, t2;pthread_create(&t1, NULL, thread_func, NULL);pthread_create(&t2, NULL, thread_func, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);return 0;
}

📌 编译命令:

gcc -o tls_example tls_example.c -lpthread

🔹 总结对比表

方法特点使用场景
互斥锁简单易用,粒度粗写操作频繁
读写锁支持并行读读多写少
条件变量配合互斥锁使用,支持等待通知生产者-消费者、状态等待
原子操作轻量级,无需锁单个变量的简单操作
线程局部存储线程独享变量日志、缓存、线程私有数据

🔹 小贴士

  • 合理选择同步机制,避免过度加锁导致性能下降。
  • 使用 RAII 或宏封装锁操作,减少死锁风险。
  • 对于高性能场景,可以考虑无锁队列、CAS算法等高级技术。

🔁 结语

线程安全是编写健壮多线程程序的基础。通过合理使用互斥锁、读写锁、条件变量、原子操作和线程局部存储,你可以有效避免多线程环境下的各种并发问题。

如果你喜欢这篇文章,请点赞/收藏/转发,后续我将持续更新更多关于 C/C++ 多线程编程的内容!


研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


http://www.lryc.cn/news/574848.html

相关文章:

  • 【Datawhale组队学习202506】YOLO-Master task04 YOLO典型网络模块
  • Python训练营-Day40-训练和测试的规范写法
  • 【Python-Day 29】万物皆对象:详解 Python 类的定义、实例化与 `__init__` 方法
  • 【Linux网络与网络编程】15.DNS与ICMP协议
  • 性能测试-jmeter实战4
  • 集成学习基础:Bagging 原理与应用
  • PyEcharts教程(009):PyEcharts绘制水球图
  • 60天python训练营打卡day41
  • Linux系统---Nginx配置nginx状态统计
  • 鸿蒙 Stack 组件深度解析:层叠布局的核心应用与实战技巧
  • AI时代工具:AIGC导航——AI工具集合
  • 接口自动化测试之pytest 运行方式及前置后置封装
  • 爬取小红书相关数据导入到excel
  • 项目需求评审报告参考模板
  • 图的拓扑排序管理 Go 服务启动时的组件初始化顺序
  • 飞往大厂梦之算法提升-day08
  • sqlserver怎样动态执行存储过程,并且返回报错
  • Java实现简易即时通讯系统
  • day41 打卡
  • 基于元学习的回归预测模型如何设计?
  • MySQL:深入总结锁机制
  • linux操作系统的软件架构分析
  • 战略调整频繁,如何快速重构项目组合
  • 原生策略与功耗方案参考
  • Android 开发问题:Wrong argument type for formatting argument ‘#2‘ in info_message
  • 马克思主义基本原理期末复习下
  • DVWA Brute Force漏洞深度分析与利用指南
  • iwebsec靶场sqli注入(2)
  • Vue3+Spring boot 前后端防抖增强方案
  • 学习记录:DAY33