当前位置:首页>Linux>在Linux线程中,如何使用共享内存+同步机制,采集读取摄像头数据?

在Linux线程中,如何使用共享内存+同步机制,采集读取摄像头数据?

  • 2026-10-11 10:12:56
在Linux线程中,如何使用共享内存+同步机制,采集读取摄像头数据?

这是线程间通信最核心、高频的部分。 因为同一个进程里的线程,本来就共享地址空间,所以线程之间最常见的通信方式不是 pipe 或消息队列,而是:

共享变量 / 共享缓冲区 + 同步机制(mutex / cond / semaphore)

高频场景:

摄像头采集线程+编码线程:

比如一个智能摄像头项目里,通常会有:

  • 采集线程:不断从摄像头采集图像帧

  • 编码线程:拿到图像帧后进行H264编码

  • 上传线程:把编码处理后的数据上传到服务器

其中,”采集线程“->”编码线程“最典型的做法就是:

共享一块缓冲区,

采集线程把最新帧写进去,

编码线程等待有”新数据“的通知,

编码线程取走数据后进行处理。

这就是典型的:

  • 共享内存

  • 互斥锁mutex

  • 条件变量cond

核心概念

共享内存:

线程共享进程地址空间,所以全局变量、堆内存、静态变量都可以被多个线程共同访问。

为什么需要同步?

因为多个线程同时访问共享变量时会出问题:

如:线程A正在写

线程B同时在读

这样同时访问共享变量,可能读到的数据就不完整。

所以要加同步机制。

常用API:

头文件:

#include <pthread.h>

互斥锁:

pthread_mutex_t mutex;   //互斥锁变量pthread_mutex_init(&mutex, NULL); //互斥锁初始化pthread_mutex_lock(&mutex);  //上锁pthread_mutex_unlock(&mutex); //解锁pthread_mutex_destroy(&mutex); //销毁

作用:

保护临界区,

同一时刻只允许一个线程操作共享数据。

条件变量:

pthread_cond_t cond;   pthread_cond_init(&cond, NULL);  //等待通知初始化pthread_cond_wait(&cond, &mutex);  //等待通知线程pthread_cond_signal(&cond);  //通知单个线程pthread_cond_broadcast(&cond);  //通知所有线程pthread_cond_destroy(&cond);

作用:

A线程等待条件成立 (消费者)

B线程条件满足后发通知 (生产者)

代码演示:单缓冲生产者消费者

功能说明: 模拟一个设备采集线程每秒产生一帧数据,处理线程收到后进行处理。

#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <string.h>/* * 模拟一帧图像数据 * 实际项目里可能是一块图像buffer、传感器采样值、音频帧等 */typedef struct {    int frame_id;            // 帧号    char payload[64];        // 模拟数据内容} Frame;/* 共享缓冲区:采集线程写,处理线程读 */static Frame g_frame;/* 标记缓冲区里是否有新数据 * 0: 没有新数据 * 1: 有新数据等待处理 */static int g_has_new_frame = 0;/* 互斥锁:保护共享变量 g_frame 和 g_has_new_frame */static pthread_mutex_t g_mutex;/* 条件变量:用于“通知有新数据了” */static pthread_cond_t g_cond;/* * 采集线程:模拟摄像头/传感器采集数据 */void *capture_thread(void *arg){    int i;for (i = 1; i <= 5; i++) {        sleep(1);  // 模拟采集耗时        pthread_mutex_lock(&g_mutex);        /*         * 如果上一帧还没被处理,这里简单等待         * 实际项目中也可以选择覆盖旧帧,取决于业务需求         */while (g_has_new_frame == 1) {            pthread_mutex_unlock(&g_mutex);            usleep(1000);  // 稍等1ms,避免纯忙等太激进            pthread_mutex_lock(&g_mutex);        }        /* 写入新数据 */        g_frame.frame_id = i;        snprintf(g_frame.payload, sizeof(g_frame.payload),"frame_data_%d", i);        g_has_new_frame = 1;printf("[capture] produce frame_id=%d, payload=%s\n",               g_frame.frame_id, g_frame.payload);        /* 通知处理线程:有新数据了 */        pthread_cond_signal(&g_cond);        pthread_mutex_unlock(&g_mutex);    }return NULL;}/* * 处理线程:等待新帧到来,然后消费 */void *process_thread(void *arg){    int count = 0;while (count < 5) {        pthread_mutex_lock(&g_mutex);        /*         * 这里必须用 while,不能用 if         * 原因:         * 1. 条件变量可能“虚假唤醒”         * 2. 被唤醒时条件未必还成立         */while (g_has_new_frame == 0) {            pthread_cond_wait(&g_cond, &g_mutex);        }        /* 取出共享数据 */        Frame local_frame;        memcpy(&local_frame, &g_frame, sizeof(Frame));        /* 标记这帧已经被消费 */        g_has_new_frame = 0;        pthread_mutex_unlock(&g_mutex);        /* 在锁外处理,减少锁占用时间 */printf("[process] consume frame_id=%d, payload=%s\n",               local_frame.frame_id, local_frame.payload);        count++;    }return NULL;}int main(void){    pthread_t tid_capture;    pthread_t tid_process;    pthread_mutex_init(&g_mutex, NULL);    pthread_cond_init(&g_cond, NULL);    pthread_create(&tid_capture, NULL, capture_thread, NULL);    pthread_create(&tid_process, NULL, process_thread, NULL);    pthread_join(tid_capture, NULL);    pthread_join(tid_process, NULL);    pthread_mutex_destroy(&g_mutex);    pthread_cond_destroy(&g_cond);return 0;}

编译:

gcc test.c -o test -lpthread

运行:

tammy@ubuntu:~/cprogramer/thread_pro$ gcc 01_mutex_cond.c -o 01_mutex_cond -lpthreadtammy@ubuntu:~/cprogramer/thread_pro$ ls01_mutex_cond  01_mutex_cond.ctammy@ubuntu:~/cprogramer/thread_pro$ ./01_mutex_cond[capture] produce frame_id=1, payload=frame_data_1[process] consume frame_id=1, payload=frame_data_1[capture] produce frame_id=2, payload=frame_data_2[process] consume frame_id=2, payload=frame_data_2[capture] produce frame_id=3, payload=frame_data_3[process] consume frame_id=3, payload=frame_data_3[capture] produce frame_id=4, payload=frame_data_4[process] consume frame_id=4, payload=frame_data_4[capture] produce frame_id=5, payload=frame_data_5[process] consume frame_id=5, payload=frame_data_5

实际工程里,更稳的是:双缓冲 / 环形缓冲

方案 A:双缓冲

  • buffer A 正在给消费者读

  • buffer B 给生产者写

  • 写完后交换索引

好处:

  • 降低“读写同一块内存”的冲突

  • 更容易保证数据完整

方案 B:环形缓冲区

  • 多个 frame slot

  • 生产者写 tail

  • 消费者读 head

  • 用计数器 + 条件变量/信号量同步

适合:

  • 有多个消费者

  • 想缓存多帧

  • 想减少丢帧

最新文章

随机文章