You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C异步编程(Vimba API场景)中管理全局变量?

问题

我正在使用Vimba.c API,基于文档中的AsynchronousGrab示例做二次开发,已经把程序改成了用户触发模式:主程序在前台运行,AsynchronousGrab在后台持续传输相机流,当main发送触发信号时,异步抓取模块会捕获一帧并处理,之后继续维持流传输。目前已经修改AsynchronousGrab.c,为捕获的帧添加了自定义处理步骤并打印结果。

主程序program.c的核心循环代码如下:

while (1) {
    // flush(stdin); 
    printf("\nInsert <c> to capture a frame or <e> to stop acquisition and press ENTER\n\n");
    char_in = getchar();
    if (char_in == 'c') {
        err = VmbFeatureCommandRun(_cameraHandle, "TriggerSoftware");
        if (VmbErrorSuccess == err)
        {
            printf("Software Trigger sent\n");
        }
        else
        {
            printf("Error: %s\n", ErrorCodeToMessage(err));
        }
    }
    else if (char_in == 'e') {
        printf("Initializing closing routine\n");
        break;
    }
    else {
        printf("Invalid input\n");
    }
    char_in = getchar();    //consumes remaining '\n' from stdin
}

现在想新增保存功能,添加了's'指令分支:

else if (char_in == 's') {
    /* some code to signal the save*/
    err = VmbFeatureCommandRun(_cameraHandle, "TriggerSoftware");
    if (VmbErrorSuccess == err)
    {
        printf("Software Trigger sent. Frame %d will be saved.\n", frameCounter);
    }
    else
    {
        printf("Error: %s\n", ErrorCodeToMessage(err));
    }
}

我的困惑是:不知道怎么给另一个.c文件发送标记来实现条件保存。最初考虑传引用,但VmbFeatureCommandRun是Vimba库函数,没法改参数;后来想过全局变量+extern声明,但异步场景下如果前一帧还没处理完就发新触发,会导致标记值混乱。于是想到用3个标记数组,搭配全局帧ID数组来匹配标记、处理结果和文件名,但担心太复杂。想问:

  • 是否应该创建两个全局数组?
  • 如果可行,是不是只需要在program.c里声明全局变量,在AsynchronousGrab.c里用extern引用,然后通过帧ID匹配所有内容?
解决方案

不推荐用多全局数组,太冗余

你的思路方向是对的,但用3个数组没必要,核心是把「是否要保存」的标记和对应的帧ID绑定,避免异步场景下的竞态问题。下面给你两种更简洁的实现方式:


方式1:用「线程安全的队列」存待保存的帧ID

这是最稳妥的方案,完全避免全局变量的竞态:

  1. 在program.c里创建一个线程安全的队列(用互斥锁+链表实现,或者系统提供的线程安全队列均可)
  2. 当用户输入's'触发时,把当前的frameCounter(要保存的帧ID)推入队列,再发送软件触发
  3. 在AsynchronousGrab的帧回调函数里,每处理完一帧,就检查队列:如果当前帧的ID和队列头部的ID匹配,就执行保存操作,然后弹出队列头部
  4. 注意:队列操作必须加锁,防止主程序和异步回调同时读写

示例伪代码:

// program.c 全局声明(或封装成结构体)
#include <pthread.h>
#include <queue>

std::queue<int> saveFrameQueue;
pthread_mutex_t queueMutex = PTHREAD_MUTEX_INITIALIZER;

// 主程序's'分支
else if (char_in == 's') {
    err = VmbFeatureCommandRun(_cameraHandle, "TriggerSoftware");
    if (VmbErrorSuccess == err)
    {
        pthread_mutex_lock(&queueMutex);
        saveFrameQueue.push(frameCounter);
        pthread_mutex_unlock(&queueMutex);
        printf("Software Trigger sent. Frame %d will be saved.\n", frameCounter);
        frameCounter++;
    }
    else
    {
        printf("Error: %s\n", ErrorCodeToMessage(err));
    }
}

// AsynchronousGrab.c 帧回调里的检查逻辑
void FrameCallback(VmbFrame_t* pFrame) {
    // 先处理帧
    ProcessFrame(pFrame);
    
    // 检查是否需要保存
    pthread_mutex_lock(&queueMutex);
    if (!saveFrameQueue.empty() && saveFrameQueue.front() == pFrame->frameID) {
        SaveFrame(pFrame);
        saveFrameQueue.pop();
    }
    pthread_mutex_unlock(&queueMutex);
    
    // 把帧还给相机
    VmbFrameQueueCapture(_cameraHandle, pFrame, FrameCallback);
}

方式2:用「原子变量+帧ID映射」简化实现

如果不想搞队列,也可以用原子变量+一个小的缓存数组(存最近几个待保存的帧ID),适合触发频率不高的场景:

  1. 在program.c里声明一个全局的原子布尔数组(或普通数组加互斥锁),数组索引对应帧ID的模值,值表示是否需要保存
  2. 触发's'时,把对应帧ID模值的数组位置设为true,发送触发
  3. 帧回调里,处理完帧后检查当前帧ID模值对应的数组值,如果是true就保存,然后设回false

示例伪代码:

// program.c 全局声明
#include <stdatomic.h>

#define MAX_PENDING_FRAMES 10
atomic_bool saveFlags[MAX_PENDING_FRAMES] = {false};
int currentFrameID = 0;

// 主程序's'分支
else if (char_in == 's') {
    err = VmbFeatureCommandRun(_cameraHandle, "TriggerSoftware");
    if (VmbErrorSuccess == err)
    {
        int targetID = currentFrameID % MAX_PENDING_FRAMES;
        atomic_store(&saveFlags[targetID], true);
        printf("Software Trigger sent. Frame %d will be saved.\n", currentFrameID);
        currentFrameID++;
    }
    else
    {
        printf("Error: %s\n", ErrorCodeToMessage(err));
    }
}

// AsynchronousGrab.c 里用extern引用
extern atomic_bool saveFlags[];
extern int currentFrameID;

void FrameCallback(VmbFrame_t* pFrame) {
    ProcessFrame(pFrame);
    
    int checkID = pFrame->frameID % MAX_PENDING_FRAMES;
    if (atomic_load(&saveFlags[checkID])) {
        SaveFrame(pFrame);
        atomic_store(&saveFlags[checkID], false);
    }
    
    VmbFrameQueueCapture(_cameraHandle, pFrame, FrameCallback);
}

对你疑问的解答

  1. 不需要两个全局数组:不管用队列还是原子数组,核心都是绑定帧ID和保存标记,一个结构就能搞定
  2. 全局变量的extern引用是可行的:只要确保全局变量在program.c里定义,AsynchronousGrab.c里用extern声明即可,但必须加线程同步(互斥锁/原子操作),否则异步场景下会出现数据混乱

内容的提问来源于stack exchange,提问作者ATSlooking4things

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 06:32:15