我有这种情况:视频必须逐帧处理,但在处理帧时,必须按顺序将输出写入文件。

我想使用 dispatch_async 将异步块触发到并发队列以加快进程,但因为这个队列是异步的,我不知道我将如何协调以串行方式将帧写入输出。

假设这种情况:帧 1、2、3、4 和 5 被发送到并发队列进行处理。因为任何块都可以在任何时间完成,所以第 4 帧可能是第一个完成的,然后是 5、3、1、2。那么我将如何设法按顺序将帧写入输出?

我有一个这样的代码:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

while (true) {

    video >> frame;  // read a frame from the video

    dispatch_async(aQueue, ^{
         processVideo(frame, outputFrame);
         writeToVideo(outputFrame); // this is here just to show what needs to be done
    });

    // bla bla

}

任何线索?

谢谢

最佳答案

我会使用串行调度队列和 NSCondition 的组合。串行队列确保没有任何写入同时发生,而 NSCondition 确保它们以正确的顺序发生。

来自 NSCondition 文档:



在你的具体情况下,我会做这样的事情......

在您的循环中,您首先声明一个 BOOL (最初设置为 NO ),它表明您的帧是否已被处理,以及一个 NSCondition 。然后,dispatch_async 到后台队列处理帧,和串行队列写入数据。

当串行队列中的块运行时,锁定 NSCondition,然后检查 BOOL 以查看该帧是否已处理。如果有,继续写入。如果没有,则从 wait 获取 signalNSCondition 并在收到它时再次检查。完成后, unlock NSCondition

当后台队列中的块运行时,锁定 NSCondition 并处理该帧。当帧被处理时,设置BOOL来表示帧被处理。然后 signalunlock NSCondition

注意: 重要的是,您只能访问表示帧已处理的 BOOLoutputFrame 锁内的 NSCondition;锁确保它们在线程之间保持同步。

// Create the background and serial queues
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t writeQueue = dispatch_queue_create("writeQueue", DISPATCH_QUEUE_SERIAL);

while (true) { // I'm assuming you have some way to break out of this...
    NSCondition *condition = [[NSCondition alloc] init];

    // These need the __block attribute so they can be changed inside the blocks
    __block BOOL frameProcessed = NO;
    __block FrameType outputFrame = nil;

    // video >> frame;  // read a frame from the video

    // dispatch the frame for processing
    dispatch_async(backgroundQueue, ^{
        [condition lock];

        processVideo(frame, outputFrame);
        frameProcessed = YES;

        [condition signal];
        [condition unlock];
    });

    // dispatch the write
    dispatch_async(writeQueue, ^{
        [condition lock];
        while (!frameProcessed) {
            [condition wait]; // this will block the current thread until it gets a signal
        }

        writeToVideo(outputFrame);

        [condition unlock];
    });
}

注意: 在上面的代码中也有一个关于 BOOL frameProcessed 的半微妙技巧。由于它是在循环内部而不是外部声明的,因此每个块都将捕获与其帧相关联的块。

更新: 添加一个 NSCondition 以供阅读。



我会通过使用另一个 NSCondition 限制读取来处理这个问题,如果有太多帧等待写入 writeQueue ,它会阻止您的读取。概念与我们之前添加的NSCondition几乎相同,只是条件不同;在此类型转换中,它将是一个 int,指示有多少帧正在等待写入。

在循环之前,定义 readConditionwriteQueueSizemaxWriteQueueSize 。在循环内部,首先 lock readCondition ,检查 writeQueueSize >= maxWriteQueueSize 。如果不是,则继续读取帧并将处理和写入排队。就在您调度到 writeQueue 之前,增加 writeQueueSize 。然后 unlock readCondition

然后,在分派(dispatch)给 writeQueue 的块内,一旦写入完成, lock readCondition ,递减 writeQueueSize ,以及 signalunlock readCondition

这应该确保在 maxWriteQueueSize 中等待的块永远不会超过 writeQueue 。如果有那么多块在等待,它会有效地暂停从视频中读取帧,直到 writeQueue 准备好接收更多帧。
// Create the background and serial queues
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t writeQueue = dispatch_queue_create("writeQueue", DISPATCH_QUEUE_SERIAL);

NSCondition *readCondition = [[NSCondition alloc] init];
__block int writeQueueSize = 0;
const int maxWriteQueueSize = 10;

while (true) { // I'm assuming you have some way to break out of this...
    NSCondition *writeCondition = [[NSCondition alloc] init];

    // These need the __block attribute so they can be changed inside the blocks
    __block BOOL frameProcessed = NO;
    __block FrameType outputFrame = nil;

    [readCondition lock];
    while (writeQueueSize >= maxWriteQueueSize) {
        [readCondition wait];
    }

    // video >> frame;  // read a frame from the video

    // dispatch the frame for processing
    dispatch_async(backgroundQueue, ^{
        [writeCondition lock];

        processVideo(frame, outputFrame);
        frameProcessed = YES;

        [writeCondition signal];
        [writeCondition unlock];
    });

    // dispatch the write
    writeQueueSize++; // Increment the write queue size here, before the actual dispatch
    dispatch_async(writeQueue, ^{
        [writeCondition lock];
        while (!frameProcessed) {
            [writeCondition wait]; // this will block the current thread until it gets a signal
        }

        writeToVideo(outputFrame);

        [writeCondition unlock];

        // Decrement the write queue size and signal the readCondition that it changed
        [readCondition lock];
        writeQueueSize--;
        [readCondition signal];
        [readCondition unlock];
    });

    [readCondition unlock];
}

关于ios - 异步执行但同步写入,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25735336/

10-12 03:38