我想构建一个NSOperation,它在开始后超时为10秒,并且可以在任何时候通过事件由另一个线程结束。我还使用NSOperationQueue来管理这样的更多操作,它一次只能计算一个(maxConcurrentOperationCount = 1)。为此,我考虑了使用dispatch_semaphore
的实现,如下所示:
@implementation CustomOperation
dispatch_semaphore_t semaphore;
-(void) main {
@autoreleasepool {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(shouldFinishWaiting:) name:@"myCustomEvent" object:nil];
semaphore = dispatch_semaphore_create(0);
[self doStuff];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)));
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"myCustomEvent" object:nil];
}
}
-(void) shouldFinishWaiting {
NSLog(@"[WatchOperation]: Should finish waiting! %@", self);
dispatch_semaphore_signal(semaphore);
}
@end
我遇到的问题是,在用户启动应用程序的许多次中,第一次操作将无法完成,直到事件被触发(这可能在30分钟后发生)。超时不会被考虑在内。我在一些用户的日志中注意到这一点,所以我无法重现它。可能出现什么问题导致dispatch_semaphore_wait
无法执行?
稍后编辑:我错误地认为-doStuff
是异步的。它似乎不是。我用以下代替:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
[self doStuff];
});
,但操作已经在串行队列'用户启动'上。正如我所看到的那样,它会创建另一个精确的线程,每次都会发生这种情况吗?这样安全吗?
答案 0 :(得分:1)
我认为调度信号量不会出错。也许你的-doStuff花了太多时间。确保您正在执行以下操作:
1.方法[self doStuff];
是异步的,它调度到一个不同于当前线程的线程(如果你想使用信号量进行10秒超时,则调度到当前线程没有意义)。
2.确保在-doStuff中检查self.isCancelled。
另外,我建议您根据自己的要求采用略有不同的设计方法(如果我理解正确的话) -
1.您的NSOperation总是可以从我的调用取消对象的任何外部线程中取消,因此不需要复杂的基于NSNotification的方法,只需检查isCancelled并覆盖-cancel方法。
2.对于10秒超时,您可以使用信号量方法,但只需要一个执行信号量等待的DIFFERENT线程。然后,该线程可以在10秒后从内部取消您的任务。
答案 1 :(得分:0)
我认为你的信号量不会成为全局变量......
根据您使用的是32位还是64位,NSEC_PER_SEC可能是32位值,它会溢出并在乘以10时变为负值。将10替换为10.0。可以解决问题,可能根本不做任何事情。
答案 2 :(得分:0)
@selector(shouldFinishWaiting :)应该是@selector(shouldFinishWaiting)。没有冒号,因为该方法没有参数。