[学习] C语言编程中线程安全的实现方法(示例)
🧵C语言编程中线程安全的实现方法
在多线程编程中,线程安全(Thread Safety) 是一个非常重要的概念。当多个线程同时访问共享资源时,如果没有合理的同步机制,就可能导致数据竞争、死锁甚至程序崩溃。
本文将详细介绍在 C语言 中如何实现线程安全的几种主要方式,并提供可以实际运行的代码示例。
文章目录
- 🧵C语言编程中线程安全的实现方法
- 🔹 一、什么是线程安全?
- 🔹 二、C语言中线程安全的实现方式
- 🔸 方法一:互斥锁(Mutex)
- ✅ 示例:用互斥锁保护计数器
- 🔸 方法二:读写锁(Read-Write Lock)
- ✅ 示例:读写锁保护共享结构体
- 🔸 方法三:条件变量(Condition Variable)
- ✅ 示例:生产者-消费者模型
- 🔸 方法四:原子操作(Atomic Operations)
- ✅ 示例:使用原子变量实现计数器
- 🔸 方法五:线程局部存储(TLS)
- ✅ 示例:使用 `__thread` 关键字(GCC扩展)
- 🔹 总结对比表
- 🔹 小贴士
- 🔁 结语
🔹 一、什么是线程安全?
线程安全是指:当多个线程并发访问某个函数或对象时,其行为仍然正确,不会因为并发访问而产生错误结果。
🔹 二、C语言中线程安全的实现方式
在C语言中,最常用的线程库是 POSIX Threads(pthread)。我们主要使用以下机制来实现线程安全:
- 互斥锁(Mutex)
- 读写锁(Read-Write Lock)
- 条件变量(Condition Variable)
- 原子操作(Atomic Operations)
- 线程局部存储(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(¬_full, &mutex);}buffer = i;count++;printf("Produced: %d\n", i);pthread_cond_signal(¬_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(¬_empty, &mutex);}int item = buffer;count--;printf("Consumed: %d\n", item);pthread_cond_signal(¬_full);pthread_mutex_unlock(&mutex);sleep(2);}return NULL;
}int main() {pthread_t prod, cons;// 初始化锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(¬_empty, NULL);pthread_cond_init(¬_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(¬_empty);pthread_cond_destroy(¬_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++ 多线程编程的内容!
研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)