我遇到这种情况:视频必须逐帧处理,但正在处理帧时,输出必须按顺序写入文件。
我想使用dispatch_async
将异步块发送到并发队列来加速进程,但由于这个队列是异步的,所以我不知道如何协调将帧连续写入输出。 / p>
假设这种情况:帧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
}
任何线索?
感谢
答案 0 :(得分:4)
我会使用串行调度队列和NSCondition
的组合。串行队列确保没有任何写入同时发生,而NSCondition
确保它们以正确的顺序发生。
来自NSCondition
文档:
条件对象充当给定的锁定和检查点 线。锁可以在测试条件时保护您的代码 执行由条件触发的任务。检查点行为 要求在线程继续之前条件为真 它的任务。虽然条件不成立,但线程会阻塞。
在您的具体情况下,我会做这样的事情......
在循环中,您首先声明BOOL
(最初设置为NO
),表示您的框架是否已被处理,以及NSCondition
。然后,dispatch_async
同时为后台队列处理帧,并将串行队列写入数据。
当串行队列中的块运行时,锁定NSCondition
,然后检查BOOL
以查看帧是否已被处理。如果有,继续写。如果没有,wait
来自signal
的{{1}}并在收到时再次检查。完成后,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
的块内,写入完成后,writeQueue
lock
,递减readCondition
,writeQueueSize
和{{1 }} signal
。
这应该确保在unlock
中等待的readCondition
块永远不会超过maxWriteQueueSize
。如果有多个块等待,它将有效地暂停从视频中读取帧,直到writeQueue
准备好更多。
writeQueue
答案 1 :(得分:0)
您可以通过为每个帧提供自己的结果队列,并按顺序将所有队列链接在一起来完成此操作。我们暂停除第一个队列之外的所有队列。然后,当每个帧完成时,它将恢复下一个结果队列。这将迫使队列按照我们想要的顺序传递结果,无论他们何时完成工作。
这是一个仅使用sleep
来模拟一些随机工作量并以正确顺序打印结果的示例。这里使用dispatch_group
来防止程序过早退出。在你的情况下你可能不需要它。
int main(int argc, const char * argv[])
{
@autoreleasepool {
dispatch_queue_t mainQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t myQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
for (unsigned x = 1; x <= 5; x++ ) {
// Chain the queues together in order; suspend all but the first.
dispatch_queue_t subQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(subQueue, myQueue);
dispatch_suspend(subQueue);
dispatch_group_async(group, mainQueue,^{
// Perform a random amount of work
u_int32_t sleepTime = arc4random_uniform(10);
NSLog(@"Sleeping for thread %d (%d)", x, sleepTime);
sleep(sleepTime);
// OK, done with our work, queue our printing, and tell the next guy he can print
dispatch_sync(myQueue, ^{
printf("%d ", x);
dispatch_resume(subQueue);
});
});
myQueue = subQueue;
}
// Wait for the whole group to finish before terminating
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
}