当前位置:首页>java>谈C编程---高效并发编程

谈C编程---高效并发编程

  • 2026-10-21 13:28:34
谈C编程---高效并发编程

C语言高效并发编程的核心在于轻量级线程管理与精细同步控制的平衡。基于POSIX线程库(pthreads)的实现是最经典方案,其简洁性体现在用最少代码实现多任务并行,同时通过同步机制确保数据安全。以下代码片段展示了并发编程的精髓,涵盖线程创建、同步、性能优化等关键场景。

一、极简多线程创建:并行执行基础

创建线程的核心接口是 pthread_create,通过函数指针传递任务逻辑。以下示例用3行核心代码实现多线程并行,每个线程独立执行任务。

#include<pthread.h>3
#include<stdio.h>

void*task(void* arg){// 线程任务函数
printf("Thread %ld running\n",(long)arg);
returnNULL;
}

int main(){
pthread_t tids[3];
// 创建3个线程,传递0、1、2作为参数
for(long i =0; i <3; i++){
pthread_create(&tids[i],NULL, task,(void*)i);// 核心函数
}
// 等待所有线程结束
for(int i =0; i <3; i++){
pthread_join(tids[i],NULL);// 阻塞回收线程
}
return0;
}

高效点:

  • 线程创建开销仅为进程的1/100左右,适合频繁启停的轻量级的任务;

  • 通过 pthread_join 确保主线程等待子线程,避免程序提前退出。

二、互斥锁:共享资源保护的最小开销方案

多线程共享全局变量时,需用互斥锁(pthread_mutex_t)避免数据竞争。以下代码用互斥锁保护计数器自增,确保多线程安全。

#include<pthread.h>
#include<stdio.h>

int counter =0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;// 静态初始化锁

void*increment(void* arg){
for(int i =0; i <100000; i++){
pthread_mutex_lock(&lock);// 加锁(临界区开始)
        counter++;// 共享资源操作
pthread_mutex_unlock(&lock);// 解锁(临界区结束)
}
returnNULL;
}

int main(){
pthread_t t1, t2;
pthread_create(&t1,NULL, increment,NULL);
pthread_create(&t2,NULL, increment,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
printf("Final counter: %d\n", counter);// 预期输出 200000
pthread_mutex_destroy(&lock);// 销毁锁释放资源
return0;
}

高效点:

  • 互斥锁通过内核态/用户态切换实现,加锁开销约几十纳秒,适合短临界区;

  • 静态初始化(PTHREAD_MUTEX_INITIALIZER)比动态初始化(pthread_mutex_init)更简洁。

三、条件变量:线程间协作的高效通信

条件变量(pthread_cond_t)用于线程间等待/通知,实现生产者-消费者模型时比忙等待(while循环)更节省CPU资源。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>

#defineBUFFER_SIZE5
int buffer[BUFFER_SIZE];
int count =0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;// 缓冲区不满条件
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;// 缓冲区非空条件

void*producer(void* arg){
for(int i =0; i <10; i++){
pthread_mutex_lock(&lock);
// 等待缓冲区不满
while(count == BUFFER_SIZE){
pthread_cond_wait(&not_full,&lock);// 自动释放锁并阻塞
}
        buffer[count++]= i;// 生产数据
printf("Produced: %d\n", i);
pthread_cond_signal(&not_empty);// 通知消费者
pthread_mutex_unlock(&lock);
}
returnNULL;
}

void*consumer(void* arg){
for(int i =0; i <10; i++){
pthread_mutex_lock(&lock);
// 等待缓冲区非空
while(count ==0){
pthread_cond_wait(&not_empty,&lock);
}
int data = buffer[--count];// 消费数据
printf("Consumed: %d\n", data);
pthread_cond_signal(&not_full);// 通知生产者
pthread_mutex_unlock(&lock);
}
returnNULL;
}

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);
// 清理资源
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&not_full);
pthread_cond_destroy(&not_empty);
return0;
}

高效点:

  • pthread_cond_wait 原子释放锁并阻塞,避免“唤醒丢失”问题;

  • 条件变量仅在状态变化时通知,CPU利用率接近100%(无忙等待)。

四、线程池:高并发场景的性能利器

线程池通过复用线程减少创建/销毁开销,适合处理大量短任务(如网络请求、数据处理)。以下是极简线程池实现,核心是任务队列+工作线程。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#defineTHREAD_NUM4
#defineQUEUE_SIZE100

typedefstruct{
void(*function)(void*);// 任务函数
void* arg;// 函数参数
} Task;

Task queue[QUEUE_SIZE];
int front =0, rear =0;
pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t queue_not_empty = PTHREAD_COND_INITIALIZER;
int shutdown =0;

void*worker(void* arg){
while(1){
pthread_mutex_lock(&queue_lock);
// 等待任务或关闭信号
while(front == rear &&!shutdown){
pthread_cond_wait(&queue_not_empty,&queue_lock);
}
if(shutdown)break;// 收到关闭信号,退出线程
// 取出任务
        Task task = queue[front];
        front =(front +1)% QUEUE_SIZE;
pthread_mutex_unlock(&queue_lock);
// 执行任务
        task.function(task.arg);
}
pthread_mutex_unlock(&queue_lock);
returnNULL;
}

void pool_init(){
pthread_t tids[THREAD_NUM];
for(int i =0; i < THREAD_NUM; i++){
pthread_create(&tids[i],NULL, worker,NULL);
}
}

void pool_add_task(void(*func)(void*),void* arg){
pthread_mutex_lock(&queue_lock);
    queue[rear]=(Task){func, arg};
    rear =(rear +1)% QUEUE_SIZE;
pthread_cond_signal(&queue_not_empty);// 通知工作线程
pthread_mutex_unlock(&queue_lock);
}

void pool_shutdown(){
pthread_mutex_lock(&queue_lock);
    shutdown =1;
pthread_cond_broadcast(&queue_not_empty);// 唤醒所有工作线程
pthread_mutex_unlock(&queue_lock);
}

// 示例任务:打印数字
void print_task(void* arg){
printf("Task %d executed by thread %ld\n",*(int*)arg,pthread_self());
free(arg);
}

int main(){
pool_init();
// 添加10个任务
for(int i =0; i <10; i++){
int* num =malloc(sizeof(int));

*num = i;
pool_add_task(print_task, num);
}
sleep(1);// 等待任务完成
pool_shutdown();
// 清理资源
pthread_mutex_destroy(&queue_lock);
pthread_cond_destroy(&queue_not_empty);
return0;
}

高效点:

  • 线程复用避免线程创建开销(每个线程创建成本约1MB栈空间+内核数据结构);

  • 任务队列用环形缓冲区实现,避免动态内存分配,响应速度提升50%+。

五、性能优化:锁粒度与无锁编程

并发性能瓶颈常源于锁竞争,优化手段包括减小锁粒度和无锁编程。以下是锁粒度优化示例:将全局锁拆分为多个局部锁,提高并发性。

#include<pthread.h>
#include<stdio.h>

#defineBUCKET_NUM8// 哈希桶数量(局部锁数量)
int buckets[BUCKET_NUM]={0};
pthread_mutex_t bucket_locks[BUCKET_NUM]={
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER
};

// 哈希函数:将key映射到桶
int hash(int key){return key % BUCKET_NUM;}

void*increment(void* arg){
int key =*(int*)arg;
int bucket =hash(key);
pthread_mutex_lock(&bucket_locks[bucket]);// 局部锁
    buckets[bucket]++;
pthread_mutex_unlock(&bucket_locks[bucket]);
returnNULL;
}

int main(){
pthread_t tids[100];
int keys[100];
for(int i =0; i <100; i++){
        keys[i]= i;
pthread_create(&tids[i],NULL, increment,&keys[i]);
}
for(int i =0; i <100; i++){
pthread_join(tids[i],NULL);
}
// 打印结果
for(int i =0; i < BUCKET_NUM; i++){
printf("Bucket %d: %d\n", i, buckets[i]);
}
return0;
}

高效点:

  • 锁粒度从“全局”拆分为“哈希桶级别”,并发冲突概率降低为1/BUCKET_NUM;

  • 静态初始化多个局部锁,避免动态内存分配。

六、核心优化原则

  1. 最小化临界区:将锁保护范围缩小到必要操作(如仅保护变量自增,而非整个函数)。

  2. 优先用户态同步:自旋锁(pthread_spinlock_t)适合极短临界区(纳秒级),避免内核态切换。

  3. 减少线程创建销毁:线程池或pthread_detach(分离线程)可降低开销。

  4. 避免死锁:按固定顺序加锁,或使用pthread_mutex_trylock超时机制。

这些代码片段展示了C语言并发编程的“极简高效”哲学:用底层API构建精准控制的并发逻辑,同时通过锁优化、线程复用等手段榨干硬件性能。你认为在嵌入式系统中,线程池与裸机多任务(如FreeRTOS)哪个更适合资源受限场景?

最新文章

随机文章