如何在C语言中使用pthreads带约束地对数组执行并发操作?
用Pthreads实现数组元素的并发加倍/减半操作(带最大值约束)
这问题在多线程同步里属于典型的条件等待场景,核心是要给每个数组元素独立的同步控制,不然一个元素触发等待会把整个数组的操作都阻塞住,完全没并发意义了。我给你拆解思路,再附上可运行的代码示例。
核心思路
- 给每个数组元素配对一个
pthread_mutex_t(互斥锁)和pthread_cond_t(条件变量):这样每个元素的操作互不干扰,前5个接近最大值的元素触发等待时,不影响后5个元素的正常操作。 - 加倍操作的逻辑:锁定对应元素的互斥锁后,循环检查元素值的2倍是否超过最大值。如果超过,就调用
pthread_cond_wait等待,直到有减半操作修改了值并发出信号;满足条件后执行加倍,最后解锁。 - 减半操作的逻辑:锁定对应元素的互斥锁后直接执行减半(问题里没提最小值限制),操作完成后调用
pthread_cond_signal唤醒可能在等待该元素的加倍线程,最后解锁。 - 为什么用
while循环检查条件?因为条件变量可能出现虚假唤醒,必须重新确认当前元素是否满足加倍的条件。
完整代码示例
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define MAX_VALUE 100 // 允许的最大值 #define ARRAY_SIZE 10 // 数组大小 #define DOUBLE_THREADS 2 // 加倍线程数量 #define HALF_THREADS 2 // 减半线程数量 // 每个元素的同步结构 typedef struct { int value; pthread_mutex_t mutex; pthread_cond_t cond; } Element; Element array[ARRAY_SIZE]; // 加倍线程函数 void* double_element(void* arg) { int thread_id = *(int*)arg; free(arg); // 释放传入的线程ID内存 for (int i = 0; i < 20; i++) { // 每个线程执行20次操作,模拟持续并发 int idx = rand() % ARRAY_SIZE; // 随机选一个元素操作 pthread_mutex_lock(&array[idx].mutex); // 循环检查:如果加倍后超过最大值,等待 while (array[idx].value * 2 > MAX_VALUE) { printf("加倍线程%d:元素%d当前值%d,加倍会超最大值,等待...\n", thread_id, idx, array[idx].value); pthread_cond_wait(&array[idx].cond, &array[idx].mutex); } // 执行加倍 array[idx].value *= 2; printf("加倍线程%d:元素%d值变为%d\n", thread_id, idx, array[idx].value); pthread_mutex_unlock(&array[idx].mutex); usleep(100000); // 模拟操作耗时,让并发效果更明显 } return NULL; } // 减半线程函数 void* half_element(void* arg) { int thread_id = *(int*)arg; free(arg); for (int i = 0; i < 20; i++) { int idx = rand() % ARRAY_SIZE; pthread_mutex_lock(&array[idx].mutex); // 直接执行减半(无最小值限制) array[idx].value /= 2; printf("减半线程%d:元素%d值变为%d\n", thread_id, idx, array[idx].value); // 唤醒等待该元素的加倍线程 pthread_cond_signal(&array[idx].cond); pthread_mutex_unlock(&array[idx].mutex); usleep(150000); } return NULL; } int main() { // 初始化数组:前5个接近最大值,后5个远低于最大值 for (int i = 0; i < ARRAY_SIZE; i++) { if (i < 5) { array[i].value = MAX_VALUE - 10; // 90,接近100 } else { array[i].value = 10; // 远低于最大值 } pthread_mutex_init(&array[i].mutex, NULL); pthread_cond_init(&array[i].cond, NULL); } pthread_t double_threads[DOUBLE_THREADS]; pthread_t half_threads[HALF_THREADS]; // 创建加倍线程 for (int i = 0; i < DOUBLE_THREADS; i++) { int* id = malloc(sizeof(int)); *id = i; pthread_create(&double_threads[i], NULL, double_element, id); } // 创建减半线程 for (int i = 0; i < HALF_THREADS; i++) { int* id = malloc(sizeof(int)); *id = i; pthread_create(&half_threads[i], NULL, half_element, id); } // 等待所有线程完成 for (int i = 0; i < DOUBLE_THREADS; i++) { pthread_join(double_threads[i], NULL); } for (int i = 0; i < HALF_THREADS; i++) { pthread_join(half_threads[i], NULL); } // 销毁同步结构 for (int i = 0; i < ARRAY_SIZE; i++) { pthread_mutex_destroy(&array[i].mutex); pthread_cond_destroy(&array[i].cond); } printf("\n所有操作完成,最终数组值:\n"); for (int i = 0; i < ARRAY_SIZE; i++) { printf("元素%d:%d\n", i, array[i].value); } return 0; }
关键细节说明
- 独立同步控制:每个元素的互斥锁和条件变量是独立的,这样当某个元素触发等待时,其他元素的线程可以正常操作,保证了并发效率。
- 虚假唤醒处理:加倍操作里用
while而非if检查条件,因为pthread_cond_wait可能在没有收到信号的情况下被唤醒(虚假唤醒),必须重新验证条件。 - 信号唤醒:减半操作完成后调用
pthread_cond_signal,只会唤醒一个等待该元素的线程;如果有多个线程等待,可以用pthread_cond_broadcast,但这里用signal足够,因为每次减半只需要一个加倍线程继续执行。 - 线程安全的随机选择:示例里用
rand()是为了简化,实际生产环境如果需要更公平的随机,建议用线程安全的随机函数(比如rand_r)。
编译运行的话,记得链接pthreads库:
gcc -o array_ops array_ops.c -lpthread ./array_ops
你可以根据实际需求调整线程数量、操作次数,或者改成每个线程固定处理几个元素,核心逻辑都是围绕每个元素的独立条件等待来做的。
内容的提问来源于stack exchange,提问作者Yos
相关产品推荐
相关产品推荐

