semaphore doesn't work as expected in my case

吃可爱长大的小学妹 提交于 2021-02-08 11:13:14

问题


This is the scenario: I want to add an observer to monitor events & when an event is triggered & processed, I wait for the result in callback block, if result is fine, I do other task. If wait timeout, I just print error message.

I use semaphore to achieve the above thing with the following simple code:

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

I call the above function in another class like this:

// 1st call
[object waitForEventThenDoOtherTask];

// this function delete local files (in another thread)
[self deleteLocalFileInAnotherThread];

// 2nd call
[object waitForEventThenDoOtherTask];

The 1st call works fine, when event is triggered and result is YES, semaphore gets the signal and waiting stops, doOtherTask is called, everything works as expected.

But for the 2nd call, when event is triggered, result is YES, semaphore gets signal, but the code is still waiting until timeout, then timeout error is printed.

Why? Why the 2nd time call code still waiting for semaphore even though semaphore gets the signal? What could be the possible reason in this context? And how to make the 2nd call work as expected as well?

===== UPDATE: I found the reason, but how to fix? ====

I realize that the callback get called multiple times, which means the same semaphore has been signaled multiple times. I think that is the reason for my problem. But, how to get rid of this problem? What is the solution then?


回答1:


I'll just explain what the semaphore does, Im sure you already know.

If you create mySemaphore with value 0, any thread that calls wait(mySemaphore) will wait until another thread does signal(mySemaphore), which increases the semaphore value by one, meaning one other thread can continue, if any was waiting.

If However you create it with value 1, any thread that calls wait(mySemaphore) will continue to run, BUT the semaphore value becomes 0 after the wait. Which means any thread that subsequently calls wait(mySemaphore) will wait until another thread calls signal(mySemaphore).

If you create mySemaphore with a value of 5, any thread that calls wait(mySemaphore) will continue to process and also reduce the semaphore value by one (making the value 4). For as long as the semaphore is not zero wait(mySemaphore) will not make the thread wait. This is useful in a number of scenarios. A thread that then calls wait(mySemaphore) when the semaphore has become 0 will then wait.

Having said this, a thread that is waiting obviously cant do anything, it will not go further than the wait(mySemaphore) point. So, if you want it to continue, another thread has to call signal(mySemaphore). The waiting thread may still not continue if another thread had called wait(mySemaphore) before it. Its like having one queue at the bank, with multiple serving point (semaphore value). A semaphore value of 0 means zero available serving points at the bank. A value of 3 means three serving points. If 5 people are waiting and one booth becomes available then the person that waited first (the one at the front of the queue) then gets served.

Now to address your case (for a start), make sure else(result==NO) also signals the semaphore...

if (result) 
{
    dispatch_semaphore_signal(semaphore);
} 
else 
{
    shouldPrintErr = YES;
    dispatch_semaphore_signal(semaphore);
}

Also Do not make the main thread wait, anything that can be lengthy and yet not UI related should be in a background thread. So change the code at the wait section to this:

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];
    }
});

UPdate:

Seeing that you fixed your initial issue, and that the remaining issue is the over-signaling of the semaphore, what you can do (I have done this before) is to create a wrapper class for dispatch_semaphore_t. I have done this but can not share the code as it legally does not belong to me. You will have an integer property on that class called maximumValue. The class will have a signal and wait method which increment or decrement an internal integer that it uses to track the value of the semaphore. The signal will determine whether to signal or not depending on the maximum value. I believe you should be able to do that anyway from semaphore._value. I hope this helps someone.



来源:https://stackoverflow.com/questions/37985337/semaphore-doesnt-work-as-expected-in-my-case

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!