我有这种情况:视频必须逐帧处理,但在处理帧时,必须按顺序将输出写入文件。
我想使用 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
获取 signal
的 NSCondition
并在收到它时再次检查。完成后, unlock
NSCondition
。
当后台队列中的块运行时,锁定 NSCondition
并处理该帧。当帧被处理时,设置BOOL
来表示帧被处理。然后 signal
和 unlock
NSCondition
。
注意: 重要的是,您只能访问表示帧已处理的 BOOL
和 outputFrame
锁内的 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
,指示有多少帧正在等待写入。在循环之前,定义
readCondition
、 writeQueueSize
和 maxWriteQueueSize
。在循环内部,首先 lock
readCondition
,检查 writeQueueSize >= maxWriteQueueSize
。如果不是,则继续读取帧并将处理和写入排队。就在您调度到 writeQueue
之前,增加 writeQueueSize
。然后 unlock
readCondition
。然后,在分派(dispatch)给
writeQueue
的块内,一旦写入完成, lock
readCondition
,递减 writeQueueSize
,以及 signal
和 unlock
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/