【问题标题】:semaphore doesn't work as expected in my case在我的情况下,信号量不能按预期工作
【发布时间】:2016-06-23 07:50:11
【问题描述】:

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

我使用 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信号量得到信号并且等待停止,doOtherTask被调用,一切都按预期工作。

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

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

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

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

【问题讨论】:

  • 函数是否在主线程调用?
  • 是的,在主线程中调用
  • @Leem.fin 第二次调用是指第二次调用方法 waitForResultThenDoOtherTask() 吗?
  • @pnizzle,是的,完全正确。所以,一般来说,我调用这个函数两次,在这之间,我调用另一个函数来删除一些本地文件,那个操作在另一个线程中。如果您阅读我在代码中的评论,您就会明白我的意思。
  • 好吧,让我看看能不能给你写点东西。

标签: ios objective-c semaphore


【解决方案1】:

我只是解释信号量的作用,我相信你已经知道了。

如果您创建值为 0 的 mySemaphore,任何调用 wait(mySemaphore) 的线程都将等待,直到另一个线程调用 signal(mySemaphore),这会将信号量值加一,这意味着如果有其他线程在等待,则可以继续。

如果您使用值 1 创建它,则任何调用 wait(mySemaphore) 的线程 将继续运行,但信号量值在等待后变为 0。这意味着随后调用wait(mySemaphore) 的任何线程都将等待直到另一个线程调用signal(mySemaphore)

如果您创建值为 5 的 mySemaphore,任何调用 wait(mySemaphore) 的线程将继续处理并将信号量值减一(使值变为 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 无关的东西都应该在后台线程中。所以把wait部分的代码改成这样:

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 做到这一点。我希望这对某人有所帮助。

【讨论】:

    猜你喜欢
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    • 2017-06-25
    • 1970-01-01
    相关资源
    最近更新 更多