这是一个场景:我想添加一个观察者来监控事件&当一个事件被触发时&处理后,我在回调块中等待结果,如果结果没问题,我还做其他任务。如果等待超时,我只是打印错误信息。
我使用信号量来实现上述功能,使用以下简单代码:
-(void)waitForResultThenDoOtherTask {
BOOL shouldPrintErr = NO;
// I create a semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// I have an observer with a callback, the callback is triggered when event is observed & result is passed to callback
[self addObserver:myObserver withCallback:callback];
id callback = ^(BOOL result) {
// if result is YES, I signal semaphore, otherwise, set shouldPrintErr flag to YES
if (result) {
dispatch_semaphore_signal(semaphore);
} else {
shouldPrintErr = YES;
}
}
// wait until timeout
dispatch_time_t timeOut = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
dispatch_semaphore_wait(semaphore, timeOut);
if (shouldPrintErr) {
NSLog(@"TIME OUT!!!");
} else {
// do other task
[self doOtherTask];
}
}
我在另一个类中调用上面的函数:
// 1st call
[object waitForEventThenDoOtherTask];
// this function delete local files (in another thread)
[self deleteLocalFileInAnotherThread];
// 2nd call
[object waitForEventThenDoOtherTask];
第一个电话工作正常,触发事件且result
为YES
,信号量获取信号并等待停止,{{ 1}}被调用,一切都按预期工作。
但是对于第二次调用,当事件被触发时,doOtherTask
为result
,信号量会收到信号,但代码仍在等待直到超时,然后打印超时错误。
为什么呢?为什么第二次调用代码仍在等待信号量,即使信号量获得信号?在这种情况下可能的原因是什么?以及如何使第二次呼叫按预期工作?
=====更新:我找到了原因,但是如何解决? ====
我意识到回调被多次调用,这意味着相同的信号量已被多次发出信号。我认为这就是我的问题的原因。但是,如何摆脱这个问题呢?那么解决方案是什么?
答案 0 :(得分:1)
我只是解释一下信号量的作用,我相信你已经知道了。
如果使用值0创建mySemaphore,则调用wait(mySemaphore)
的任何线程将等到另一个线程执行signal(mySemaphore)
,这会将信号量值增加1,这意味着另一个线程可以继续,如果有的话等待。
如果您使用值1创建它,则调用wait(mySemaphore)
的任何线程
将继续运行,但等待后信号量值变为0。这意味着随后调用wait(mySemaphore)
的任何线程将等到另一个线程调用signal(mySemaphore)
。
如果您创建值为5的mySemaphore,则任何调用wait(mySemaphore)
的线程都将继续处理,并将信号量值减少1(使值为4)。只要信号量不为零,wait(mySemaphore)
就不会使线程等待。这在许多场景中都很有用。然后,当信号量变为0时调用wait(mySemaphore)
的线程将等待。
说完这个,一个等待的线程显然无法做任何事情,它不会超过wait(mySemaphore)
点。因此,如果您希望它继续,另一个线程必须调用signal(mySemaphore)
。如果另一个线程在它之前调用wait(mySemaphore)
,则等待线程可能仍然无法继续。它就像在银行有一个队列,有多个服务点(信号量值)。信号量值为0表示银行的零可用服务点。值3表示三个服务点。如果5个人正在等待并且有一个展位可用,则首先等待的人(队列前面的那个人)将获得服务。
现在解决您的情况(首发),请确保else(result==NO)
也发出信号... ...
if (result)
{
dispatch_semaphore_signal(semaphore);
}
else
{
shouldPrintErr = YES;
dispatch_semaphore_signal(semaphore);
}
同样不要让主线程等待,任何可能冗长而且与UI无关的东西都应该在后台线程中。因此,请将等待部分的代码更改为:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
dispatch_time_t timeOut = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
dispatch_semaphore_wait(semaphore, timeOut);
if (shouldPrintErr)
{
NSLog(@"TIME OUT!!!");
}
else
{
// do other task, dispatch back onto main thread if required.
[self doOtherTask];
}
});
<强>更新强>
看到你修复了你的初始问题,剩下的问题是信号量的过度信号,你能做什么(我以前做过)就是为dispatch_semaphore_t创建一个包装类。我已经这样做但不能共享代码,因为它在法律上不属于我。您将在该类上有一个名为maximumValue的整数属性。该类将具有信号和等待方法,该方法递增或递减用于跟踪信号量值的内部整数。信号将根据最大值确定是否发信号。我相信你应该能够从semaphore._value那里做到这一点。我希望这有助于某人。