信号量在我的案例

时间:2016-06-23 07:50:11

标签: ios objective-c semaphore

这是一个场景:我想添加一个观察者来监控事件&当一个事件被触发时&处理后,我在回调块中等待结果,如果结果没问题,我还做其他任务。如果等待超时,我只是打印错误信息。

我使用信号量来实现上述功能,使用以下简单代码:

-(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];

第一个电话工作正常,触发事件且resultYES信号量获取信号并等待停止,{{ 1}}被调用,一切都按预期工作。

但是对于第二次调用,当事件被触发时,doOtherTaskresult信号量会收到信号,但代码仍在等待直到超时,然后打印超时错误。

为什么呢?为什么第二次调用代码仍在等待信号量,即使信号量获得信号?在这种情况下可能的原因是什么?以及如何使第二次呼叫按预期工作?

=====更新:我找到了原因,但是如何解决? ====

我意识到回调被多次调用,这意味着相同的信号量已被多次发出信号。我认为这就是我的问题的原因。但是,如何摆脱这个问题呢?那么解决方案是什么?

1 个答案:

答案 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那里做到这一点。我希望这有助于某人。