有几种方法可以在不同的抽象级别上做你想做的事情,使用 NSLock、调度障碍、NSOperation 依赖等等。但是由于您已经在使用 GCD,因此这些dispatch_semaphore_*
功能将满足您的需求。
类似的东西(在我的脑海中,可能有错别字:)
// this is the barrier one task will use to signal others that it's done.
dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(0);
__block NSString *result = @"not done yet";
// adjust these to change which task "wins":
int hardTaskLength = 3;
int timeoutSeconds = 5;
// this is the first task:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// do something slow and difficult
sleep(hardTaskLength);
result = @"now I am done";
// then when we're done, let the world know:
dispatch_semaphore_signal(mySemaphore);
});
// and this is the second, dependent one:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutSeconds * NSEC_PER_SEC));
// wait for the first task to complete, or give up after a bit:
BOOL timedOut = dispatch_semaphore_wait(mySemaphore, timeout);
// now do stuff that wants the result of the first task
if (timedOut)
NSLog(@"Gave up waiting after %d seconds, result is: %@", timeoutSeconds, result);
else
NSLog(@"Processing finished; result is: %@", result);
// these can happen wherever is appropriate, after everything is done:
dispatch_release(mySemaphore);
mySemaphore = 0;
});
手册部分“Grand Central Dispatch 参考”包含有关信号量如何工作的更多信息。信号量本质上是一个线程安全的计数器;“发信号”将其加一,“等待”将其减一……除非计数器为零;然后“等待”停止并等待,直到其他东西再次增加计数器,然后递减并继续。
来自文档:“[在 dispatch semaphore_create] 中为值传递零对于两个线程需要协调特定事件的完成时很有用。” 这正是您正在做的事情。
[编辑添加]:
但是,根据问题中的其他信息,调度队列看起来对你正在做的事情来说太过分了。无论如何 UIKit 都必须在主线程上发生,所以请执行以下操作:
- 在 5 秒开始的时候,在你关心的属性上设置 KVO,并启动一个 5 秒的 NSTimer。
- 在KVO监听器中,拍照,取消定时器。
- 在计时器处理程序方法中,拍照,并注销 KVO 观察。
- 如果首先发生异常情况(取消按钮?)并且您需要中止,则两者都取消注册 KVO 取消计时器。
由于一切都发生在主线程上,因此不必担心争用,并且计时器会处理防止主线程阻塞的情况。