NSOperationQueue,并发操作和线程

时间:2013-12-23 07:05:54

标签: ios multithreading grand-central-dispatch nsoperation nsoperationqueue

我正在开发一种快速图像扫描应用程序 在-captureOutput:didOutputSampleBuffer:fromConnection:方法中,我选取CVPixelBuffer并将它们添加到NSOperation子类中。 NSOperation获取缓冲区并将其转换为保存在文件系统上的图像 -isConcurrent NSOperation方法返回YES。创建操作后,将添加到NSOperationQueue 除了影响扫描帧速率的一个问题外,一切运行正常 使用时间分析器我发现一些NSOperation运行在我创建的AVCaputureVideoOutput委托的同一个线程上:

dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_SERIAL);
[dataOutput setSampleBufferDelegate:(id)self queue:queue];

当一个操作在AV会话队列的同一个线程上运行时,它会影响帧速率,它只发生在它们中,可能GCD正在决定在活动线程上调度。
唯一的方法我发现要解决这个问题,就是创建一个单独的线程并将其传递给单个操作,强制它们在它上面运行。
还有另一种方法,强制在不同的线程上运行操作吗?

[编辑]
按照@impcc的建议,我做了一些测试 即使有些不一致,结果也很有趣 测试平台是一个通过MBP与调试模式连接的iPhone 5,具有16GB的RAM四核i7。会话使用RosyWriter apple示例代码中的算法测试了60fps的输出。
AVSession队列同步针对高优先级队列

  • 26 fps,有时队列的线程与该队列共享 的操作。委托方法内部的时间有一个 平均0.02s

  • 14 fps,主要是为操作创建的线程 在此线程上没有调用方法,将强制启动执行 在那个特定的线程上。这个线程创建一次并保留 用虚拟端口活着..代表内部的时间是 0.008。

AVSession队列并发目标为高优先级队列

  • 13.5 fps,有时队列的线程与其中一个操作共享。委托方法内部所花费的时间 平均0.02s,基本等于同步的对应物 队列中。

  • 14 fps,主要是为操作创建的线程 在此线程上没有调用方法,将强制启动执行 在那个特定的线程上。这个线程创建一次并保留 活着的虚拟端口。代表内部的时间是 0.008点。

结论
并发或串行队列似乎没有太大的区别,但是对我而言并发是不正确的,因为我需要采取时间戳来保留单个图片的序列。让我感到惊讶的事实是使用ad-hoc线程的帧丢弃,即使委托方法内部所花费的时间相当少,帧速率也会下降大约10fps。只是信任GCD,帧速率更好,但委托方法需要2次以上才能完成,这可能是因为有时AV队列也被nsoperations使用。我真的不明白为什么代表内部的时间似乎与fps无关。不应该越快越好?
通过进一步调查,似乎在添加和可能在队列中执行操作的过程中窃取时间si是什么。

1 个答案:

答案 0 :(得分:8)

我认为你可能会误解isConcurrent的含义。这完全可以理解,因为它的命名非常糟糕。从YES返回-isConcurrent时,它的真正含义是“我将处理与此操作相关的任何并发需求,否则将异步运行。”在这种情况下,NSOperationQueue可以在您添加操作的线程上同步调用-start您的操作。 NSOperationQueue期待这一点,因为您已声明将管理自己的并发性,因此您的-start方法将启动异步进程并立即返回。我怀疑这是你问题的根源。

如果您通过覆盖-main来实施操作,那么您几乎肯定希望从NO返回isConcurrent。为了使问题更复杂,与isConcurrent相关的行为多年来发生了变化(但官方文档中涵盖了所有这些行为。)梦幻解释如何正确实施退货 - 可以找到YES - 来自 - isConcurrent NSOperation here.

我在这里阅读你的问题的方式,听起来不像你的NSOperation子类实际上需要“管理它自己的并发”,而是你只是想让它以异步方式执行,“后台线程“,可能与其他操作同时”。

在确保AVCaptureVideoDataOutputSampleBufferDelegate回调的最佳性能方面,我建议将传递给-setSampleBufferDelegate:queue:的队列(本身)并发,并将其作为高优先级全局并发队列的目标,像这样:

dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));

[dataOutput setSampleBufferDelegate:(id)self queue:queue];

然后你应该让委托回调方法尽可能轻量级 - 只需打包制作NSOperation所需的信息并将其添加到NSOperationQueue

这应确保回调始终优先于NSOperations。 (我的理解是NSOperationQueue定位主队列(对于与主线程和运行循环相关联的NSOperationQueue)或默认优先级后台队列。)这应该允许您的回调完全跟上与帧率。

另一个重要的事情就是拿走(另一位评论者得到的)是,如果你使用GCD进行所有并发操作,那么只有一个特殊线程 - 主线程。除此之外,线程只是GCD可以(并且将)彼此互换使用的通用资源。事实上,一个点使用了一个线程ID为X的线程来为你的委托回调服务,而在另一个点上用来进行处理的事实本身并不表示存在问题。

希望这有帮助!

相关问题