首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01|快速回顾:一个 C 程序的完整生命周期
02|程序基石:数据与量值是如何被组织的?
03|计算单元:运算符是如何工作的?
04|控制逻辑:表达式和语句是如何协调程序运行的?
05|代码封装:函数是如何被调用的?
06|整合数据:枚举、结构与联合是如何实现的?
07|操控资源:指针是如何灵活使用内存的?
08|编译准备:预处理器是怎样处理程序代码的?
09|标准库:字符、字符串处理与数学计算
10|标准库:深入理解标准 IO
11|标准库:非本地跳转与可变参数是怎样实现的?
12|标准库:你需要了解的 C 并发编程基础知识有哪些?
13|标准库:如何使用互斥量等技术协调线程运行?
14|标准库:信号与操作系统软中断有什么关系?
15|标准库:日期、时间与实用函数
16|标准库:断言、错误处理与对齐
17|极致优化:如何实现高性能的 C 程序?
18|生产加速:C 项目需要考虑的编码规范有哪些?
19|生产加速:如何使用自动化测试确保 C 项目质量?
20|生产加速:如何使用结构化编译加速 C 项目构建?
21|实战项目:一个简单的高性能 HTTP Server
22|可执行二进制文件里有什么?
23|进程是如何使用操作系统内存的?
24|编译器在链接程序时发生了什么?
25|程序可以在运行时进行链接吗?
26|C 程序的入口真的是 main 函数吗?
27|ABI 与 API 究竟有什么区别?
28|程序如何与操作系统交互?
当前位置:
首页>>
技术小册>>
深入C语言和程序运行原理
小册名称:深入C语言和程序运行原理
### 第十三章 标准库:如何使用互斥量等技术协调线程运行 在编写并发程序时,如何有效地管理线程间的同步与互斥,是确保程序正确性和性能的关键。C语言标准库本身并不直接提供高级的多线程编程接口,但POSIX线程(pthread)库作为在UNIX及类UNIX系统上广泛使用的线程库,为C语言程序提供了丰富的线程同步机制,包括互斥量(Mutexes)、条件变量(Condition Variables)、信号量(Semaphores)等。本章将重点介绍如何使用这些技术,特别是互斥量,来协调线程的运行。 #### 13.1 线程同步与互斥的基础 **13.1.1 线程同步的概念** 线程同步是指多个线程在执行过程中,按照一定的顺序或条件相互协作,以完成共同的任务。它确保了在多线程环境下,数据的访问和修改是安全的,避免了数据竞争(Race Conditions)和死锁(Deadlocks)等问题。 **13.1.2 互斥量的作用** 互斥量(Mutex,Mutual Exclusion的缩写)是一种用于保护共享资源的数据结构,它确保在任何时刻,只有一个线程可以访问该资源。当一个线程想要访问被互斥量保护的资源时,它必须先锁定该互斥量;访问结束后,再解锁互斥量,以便其他线程可以访问。 #### 13.2 POSIX线程库中的互斥量 在POSIX线程库中,互斥量的相关操作主要通过以下几个函数实现: - `pthread_mutex_init()`:初始化互斥量。 - `pthread_mutex_lock()`:尝试锁定互斥量。如果互斥量已被锁定,则调用线程将被阻塞,直到互斥量被解锁。 - `pthread_mutex_unlock()`:解锁互斥量,允许其他线程访问被保护的资源。 - `pthread_mutex_destroy()`:销毁互斥量,释放其占用的资源。 **示例代码**: ```c #include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_mutex_t lock; int counter = 0; void* increment(void* arg) { pthread_mutex_lock(&lock); counter++; printf("Counter = %d\n", counter); pthread_mutex_unlock(&lock); return NULL; } int main() { pthread_t t1, t2; pthread_mutex_init(&lock, NULL); // 初始化互斥量 pthread_create(&t1, NULL, increment, NULL); pthread_create(&t2, NULL, increment, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_mutex_destroy(&lock); // 销毁互斥量 return 0; } ``` 在上述示例中,两个线程都试图增加同一个全局变量`counter`的值。通过使用互斥量`lock`,我们确保了每次只有一个线程能访问并修改`counter`,从而避免了数据竞争。 #### 13.3 互斥量的属性与类型 POSIX线程库允许通过`pthread_mutexattr_t`结构体和相关的函数来设置互斥量的属性,如类型(如快速互斥量、递归互斥量等)。 - **快速互斥量(Default Mutex)**:标准的互斥量类型,适用于大多数情况。 - **递归互斥量(Recursive Mutex)**:允许同一个线程多次锁定同一个互斥量,而不会导致死锁。每次锁定操作都需要对应的解锁操作来解锁。 **设置互斥量属性示例**: ```c pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&lock, &attr); pthread_mutexattr_destroy(&attr); ``` #### 13.4 使用条件变量与互斥量协同工作 条件变量(Condition Variables)通常与互斥量一起使用,以等待或通知线程某些条件的发生。条件变量允许线程挂起(阻塞)在一个条件上,直到另一个线程修改了条件并通知它。 **关键函数**: - `pthread_cond_init()`:初始化条件变量。 - `pthread_cond_wait()`:在调用线程上阻塞,等待条件变量的条件为真,同时释放与之关联的互斥量。 - `pthread_cond_signal()`:唤醒等待该条件变量的一个线程。 - `pthread_cond_broadcast()`:唤醒等待该条件变量的所有线程。 **示例**:生产者-消费者问题(简化版) ```c #include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int buffer = 0; int full = 0; void* producer(void* arg) { while (1) { pthread_mutex_lock(&mutex); while (full) { pthread_cond_wait(&cond, &mutex); // 等待缓冲区不满 } buffer = 1; // 生产数据 full = 1; printf("Produced\n"); pthread_cond_signal(&cond); // 通知消费者 pthread_mutex_unlock(&mutex); sleep(1); // 模拟耗时操作 } return NULL; } void* consumer(void* arg) { while (1) { pthread_mutex_lock(&mutex); while (!full) { pthread_cond_wait(&cond, &mutex); // 等待缓冲区有数据 } buffer = 0; // 消费数据 full = 0; printf("Consumed\n"); pthread_cond_signal(&cond); // 可选,如果多个消费者可能需要 pthread_mutex_unlock(&mutex); sleep(1); // 模拟耗时操作 } return NULL; } int main() { pthread_t prod, cons; pthread_create(&prod, NULL, producer, NULL); pthread_create(&cons, NULL, consumer, NULL); pthread_join(prod, NULL); pthread_join(cons, NULL); // 实际应用中,通常不会在这里等待终止 return 0; } ``` 在这个示例中,生产者和消费者通过互斥量和条件变量协同工作,确保了数据的正确生产和消费,避免了数据竞争和死锁的问题。 #### 13.5 注意事项与最佳实践 1. **避免死锁**:确保互斥量的锁定顺序在所有线程中保持一致,避免循环等待条件。 2. **使用锁的范围最小化**:只在必要时锁定互斥量,并在最短时间内释放,以减少线程阻塞时间。 3. **检查返回值**:线程同步函数的返回值提供了操作是否成功的反馈,应总是检查这些值。 4. **使用递归锁需谨慎**:虽然递归锁允许同一个线程多次锁定,但过度使用可能会导致性能问题或隐藏的逻辑错误。 5. **考虑使用高级同步机制**:对于复杂的同步需求,如读者-写者问题,可以考虑使用读写锁(Read-Write Locks)等更高级的同步机制。 通过以上内容的介绍,您应该已经对如何在C语言中使用POSIX线程库中的互斥量等技术来协调线程运行有了较为全面的了解。在实际编程中,根据具体需求选择合适的同步机制,并遵循最佳实践,是编写高效、稳定并发程序的关键。
上一篇:
12|标准库:你需要了解的 C 并发编程基础知识有哪些?
下一篇:
14|标准库:信号与操作系统软中断有什么关系?
该分类下的相关小册推荐:
c++零基础入门