4. QNX 消息传递通道与脉冲机制实战
任务目标
在QNX Neutrino RTOS中,使用条件变量(pthread_cond_t)与互斥锁(pthread_mutex_t)实现经典的生产者-消费者模型。核心目标是:当缓冲区为空时,消费者线程必须阻塞等待,直到生产者写入数据并发出信号后才被唤醒继续消费,从而避免忙等待和无效的资源竞争。
核心实现
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFFER_SIZE 5
/* 共享缓冲区与同步原语 */
static int buffer[BUFFER_SIZE];
static int count = 0; /* 当前缓冲区中元素个数 */
static int in = 0; /* 生产者写入位置 */
static int out = 0; /* 消费者读取位置 */
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_producer = PTHREAD_COND_INITIALIZER; /* 缓冲区满时生产者等待 */
static pthread_cond_t cond_consumer = PTHREAD_COND_INITIALIZER; /* 缓冲区空时消费者等待 */
/* 生产者线程:每500ms生产一个数据 */
static void* producer(void* arg) {
int value = 0;
(void)arg;
while (1) {
pthread_mutex_lock(&mutex);
/* 缓冲区满则等待消费者取走数据 */
while (count == BUFFER_SIZE) {
pthread_cond_wait(&cond_producer, &mutex);
}
buffer[in] = value;
in = (in + 1) % BUFFER_SIZE;
count++;
printf("[生产者] 生产: %d (当前数量: %d)\n", value, count);
/* 唤醒一个等待中的消费者 */
pthread_cond_signal(&cond_consumer);
pthread_mutex_unlock(&mutex);
value++;
usleep(500 * 1000); /* 500ms */
}
return NULL;
}
/* 消费者线程:每1秒消费一个数据 */
static void* consumer(void* arg) {
(void)arg;
while (1) {
pthread_mutex_lock(&mutex);
/* 缓冲区空则等待生产者产生数据 */
while (count == 0) {
printf("[消费者] 缓冲区为空,等待生产者...\n");
pthread_cond_wait(&cond_consumer, &mutex);
}
int data = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
printf("[消费者] 消费: %d (剩余数量: %d)\n", data, count);
/* 唤醒一个等待中的生产者 */
pthread_cond_signal(&cond_producer);
pthread_mutex_unlock(&mutex);
sleep(1); /* 1秒 */
}
return NULL;
}
int main(void) {
pthread_t tid_producer, tid_consumer;
/* 创建两个线程 */
pthread_create(&tid_producer, NULL, producer, NULL);
pthread_create(&tid_consumer, NULL, consumer, NULL);
/* 等待线程结束(本例中线程永不退出,仅用于演示) */
pthread_join(tid_producer, NULL);
pthread_join(tid_consumer, NULL);
return 0;
}
编译与运行
# 在QNX目标机上(或QNX SDP的qcc交叉编译环境)
qcc -o condvar_demo condvar_demo.c -lpthread -lrt
./condvar_demo
验证方法与判定标准
运行程序后,观察终端输出,满足以下条件即判定同步逻辑正确:
- 消费者不会空转:当缓冲区为空时,消费者打印“缓冲区为空,等待生产者...”后阻塞,不会继续打印“消费”行。
- 生产者唤醒消费者:生产者每次写入数据后打印“生产”行,随后消费者被唤醒并打印“消费”行。
- 缓冲区数量守恒:任意时刻,“当前数量”与“剩余数量”之和不超过
BUFFER_SIZE(5),且两者均不会出现负值或超过5。 - 无死锁:程序持续运行,生产者与消费者交替执行,不会卡死。
由于生产者生产间隔(500ms)快于消费者消费间隔(1s),可观察到缓冲区数量逐渐累积至满,此时生产者会等待消费者取走数据——这正是条件变量解决“消费者等待生产者”与“生产者等待消费者”双向同步的典型表现。
关键说明
- 使用
while而非if检查条件,防止虚假唤醒(spurious wakeup)导致条件不满足时继续执行。 - 条件变量必须与互斥锁配合使用:
pthread_cond_wait原子地释放互斥锁并阻塞,被唤醒后重新获取锁。 - 本例使用两个条件变量分别管理“缓冲区满”和“缓冲区空”两种等待队列,避免生产者与消费者互相唤醒造成的无效竞争。
- 在QNX Neutrino中,
pthread_cond_signal至少唤醒一个等待线程;若需唤醒全部等待者(如多个消费者),可改用pthread_cond_broadcast。