C 编程中的多线程使开发人员能够充分利用现代多核处理器的潜力,促进单个进程中任务的并发执行。本综合指南探讨了基本的多线程概念、同步机制和高级主题,为每个概念提供了详细的解释和示例代码。
线程是进程内独立的执行序列,允许并发执行任务。了解线程的创建、管理和状态对于有效的多线程处理至关重要。
线程创建:
pthread_create():初始化一个新线程并开始执行。
pthread_join():等待线程终止后再继续。
#include#include 无效*threadFunc(无效*arg){ printf("来自新线程的你好!n"); pthread_exit(NULL); } int main() { pthread_t tid; pthread_create(&tid, NULL, threadFunc, NULL); pthread_join(tid, NULL); printf("回到主线程.n"); 返回0; }
当多个线程同时访问共享资源时,会出现竞争条件,从而导致不可预测的行为。互斥体、信号量、条件变量等同步机制保证线程安全。
互斥体(互斥):
互斥体提供互斥,一次只允许一个线程访问共享资源。它们可以防止数据损坏并确保行为一致。
#include#include pthread_mutex_t 互斥体 = PTHREAD_MUTEX_INITIALIZER; int 共享变量 = 0; 无效*threadFunc(无效*arg){ pthread_mutex_lock(&mutex); 共享变量++; printf("线程将共享变量增加到:%dn",共享变量); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main() { pthread_t tid; pthread_create(&tid, NULL, threadFunc, NULL); pthread_mutex_lock(&mutex); 共享变量--; printf("主线程将共享变量递减为:%dn",共享变量); pthread_mutex_unlock(&mutex); pthread_join(tid, NULL); 返回0; }
信号量:
信号量是用于控制对共享资源的访问并协调多个线程的执行的同步原语。他们维护一个计数来限制同时访问资源的线程数量。
#include#include #include <信号量.h> sem_t 信号量; 无效*threadFunc(无效*arg){ sem_wait(&信号量); printf("线程获取信号量"); // 临界区 sem_post(&信号量); pthread_exit(NULL); } int main() { pthread_t tid; sem_init(&信号量, 0, 1); // 用值 1 初始化信号量 pthread_create(&tid, NULL, threadFunc, NULL); // 主线程 sem_wait(&信号量); printf("主线程获取信号量"); // 临界区 sem_post(&信号量); pthread_join(tid, NULL); 返回0; }
线程通信有利于线程之间的协调和同步。条件变量允许线程等待满足特定条件。
条件变量:
条件变量使线程能够等待特定条件的发生。它们通常用于生产者-消费者场景,其中线程在继续之前等待数据可用性。
#include#include pthread_mutex_t 互斥体 = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t condVar = PTHREAD_COND_INITIALIZER; int 数据就绪 = 0; 无效*生产者(无效*参数){ pthread_mutex_lock(&mutex); 数据就绪=1; pthread_cond_signal(&condVar); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } 无效*消费者(无效*参数){ pthread_mutex_lock(&mutex); while (!dataReady) { pthread_cond_wait(&condVar, &mutex); } printf("消费者:数据已准备好!n"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main() { pthread_t 生产者线程,消费者线程; pthread_create(&生产者线程,NULL,生产者,NULL); pthread_create(&consumerThread, NULL, 消费者, NULL); pthread_join(生产者线程,NULL); pthread_join(consumerThread, NULL); 返回0; }
优先级反转、饥饿、死锁和自旋锁等高级主题对于构建健壮的多线程应用程序至关重要。
优先级反转:
当低优先级线程持有高优先级线程所需的资源时,就会发生优先级反转,从而导致优先级反转。优先级继承协议通过暂时将低优先级线程的优先级提高到高优先级线程的优先级来帮助缓解此问题。
#include#include pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; 无效*highPriorityThread(无效*arg){ pthread_mutex_lock(&mutex1); pthread_mutex_lock(&mutex2); // 执行高优先级任务 pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); pthread_exit(NULL); } 无效*lowPriorityThread(无效*arg){ pthread_mutex_lock(&mutex2); pthread_mutex_lock(&mutex1); // 执行低优先级任务 pthread_mutex_unlock(&mutex1); pthread_mutex_unlock(&mutex2); pthread_exit(NULL); } int main() { pthread_t 高优先级,低优先级; pthread_create(&highPrioTid, NULL, highPriorityThread, NULL); pthread_create(&lowPrioTid, NULL, lowPriorityThread, NULL); pthread_join(highPrioTid, NULL); pthread_join(lowPrioTid, NULL); 返回0; }
饥饿:
当一个线程由于其他线程不断获取所需资源而无法访问这些资源时,就会发生饥饿。公平的调度策略确保所有线程都有公平的资源分配机会,防止饥饿。
#include#include pthread_mutex_t 互斥体 = PTHREAD_MUTEX_INITIALIZER; int 共享资源 = 0; 无效*threadFunc(无效*arg){ pthread_mutex_lock(&mutex); // 增加共享资源 共享资源++; printf("线程将共享资源增加到:%dn",共享资源); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main() { pthread_t tid1, tid2; // 创建两个线程 pthread_create(&tid1, NULL, threadFunc, NULL); pthread_create(&tid2, NULL, threadFunc, NULL); // 等待两个线程完成 pthread_join(tid1, NULL); pthread_join(tid2, NULL); // 主线程 pthread_mutex_lock(&mutex); // 访问共享资源 printf("主线程访问了sharedResource:%dn",sharedResource); pthread_mutex_unlock(&mutex); 返回0; }
死锁:
当两个或多个线程无限期地等待对方释放它们所需的资源时,就会发生死锁。避免循环等待并实施死锁检测和恢复机制有助于缓解死锁情况。
#include#include pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; 无效*线程1(无效*参数){ pthread_mutex_lock(&mutex1); pthread_mutex_lock(&mutex2); // 临界区 pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); pthread_exit(NULL); } 无效*线程2(无效*参数){ pthread_mutex_lock(&mutex2); pthread_mutex_lock(&mutex1); // 潜在的死锁点 // 临界区 pthread_mutex_unlock(&mutex1); pthread_mutex_unlock(&mutex2); pthread_exit(NULL); } int main() { pthread_t tid1,tid2; pthread_create(&tid1, NULL, thread1, NULL); pthread_create(&tid2, NULL, thread2, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); 返回0; }
自旋锁:
自旋锁是同步原语,其中线程不断轮询资源的可用性。它们对于短关键部分和低争用场景非常有效。
#include#include pthread_spinlock_t 自旋锁; 无效*threadFunc(无效*arg){ pthread_spin_lock(&spinlock); // 临界区 printf("线程获得了自旋锁n"); // 执行一些任务 pthread_spin_unlock(&spinlock); pthread_exit(NULL); } int main() { pthread_t tid1, tid2; pthread_spin_init(&spinlock, 0); pthread_create(&tid1, NULL, threadFunc, NULL); pthread_create(&tid2, NULL, threadFunc, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_spin_destroy(&spinlock); 返回0; }
掌握 C 编程中的多线程需要深入理解基本概念、同步机制和高级主题。通过深入研究这些概念并探索示例代码,开发人员可以构建健壮、高效且响应迅速的多线程应用程序。持续实践、实验和遵守最佳实践是精通多线程和开发充分利用现代硬件功能的可靠软件系统的关键。