【问题标题】:dispatch_semaphore_wait does not wait on semaphoredispatch_semaphore_wait 不等待信号量
【发布时间】:2014-10-21 03:05:52
【问题描述】:

我开发了以下方法,用于检查应用与服务器通信的能力。 该方法执行一个简单的查询,并且知道如果它得到结果,则应该连接应用程序(基本 ping 机制)。

- (BOOL)isAppConnected
{
    __block BOOL isConnected = NO;

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    [[SFRestAPI sharedInstance] performSOQLQuery:@"SELECT id FROM Account LIMIT 1"
                                           failBlock:^(NSError *e) {
                                               isConnected = NO;
                                               NSLog(@"NOT CONNECTED %@", e);
                                               NSLog(@"fail block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);

                                               dispatch_semaphore_signal(semaphore);

                                           } completeBlock:^(NSDictionary *dict) {
                                               isConnected = YES;
                                               NSLog(@"%@", dict);
                                               NSLog(@"complete block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);

                                               dispatch_semaphore_signal(semaphore);
                                           }];

    // if the wait times-out we will receive a non-zero result and can assume no connection to SF
    //When using: DISPATCH_TIME_FOREVER the app hangs forever!!
    int waitResult = dispatch_semaphore_wait(semaphore, 30 * NSEC_PER_SEC); 
    NSLog(@"waitResult: %d", waitResult);

    return isConnected;
}

我正在使用 Apple documentation 中建议的“dispatch_semaphore_wait”

我的目标是等待响应或短暂的超时,以确定我们是否真的有一个有效的连接。

使用上面的代码,“dispatch_semaphore_wait”实际上不会等待,即执行不会在该行停止,而是会立即继续(总是将 49 作为结果返回给 dispatch_semaphore_wait 调用)。那是除非我使用 DISPATCH_TIME_FOREVER 在这种情况下应用程序永远挂起......

目前我正在从主线程调用此方法。我知道这是一个坏主意,但我希望在重构之前看到它按预期工作。

什么可能导致这种行为? 谢谢。

【问题讨论】:

  • 你从主线程调用它。这就是问题
  • 您是否尝试过在两个 dispatch_semaphore_signal 调用上设置断点的老式方法?他们是否在等待执行之前发出信号?
  • @PetroKorienev 是的,确实如此。我在方法的开头和回调中添加了一些调试 NSLog 语句,以打印出它们正在运行的线程。该方法在主线程上运行,而回调在不同的线程上执行。我可以猜测在主线程上执行等待会挂起应用程序,但回调不应该仍然执行并发出等待语句再次释放执行的信号吗?
  • @gnasher729:是的,我确实做到了。如果我将超时设置为无限,则回调永远不会执行。但是,如果我按照上面示例中的方式设置超时,则等待首先执行并跳过(我猜它超时太早了,因为您在下面的答案中描述的原因),稍后,回调执行.但是,到那时,等待已经超时,并且信号不会影响我正在等待的执行线程。

标签: ios objective-c iphone semaphore


【解决方案1】:

dispatch_semaphore_wait 的参数不是延迟,而是信号量应该唤醒的时间。你会在 1 月 1 日午夜后 30 秒醒来。 1970 年(或 2001 年,不确定)。使用 dispatch_time 函数。

【讨论】:

  • 谢谢!我完全误解了超时的用法。我已经解决了这个问题,但是我认为我的主要问题与超时设置无关,而是与我在主线程上等待的事实有关?我这样说是因为当我将超时设置为 DISPATCH_TIME_FOREVER 时,等待会永远挂起。
  • 使用 dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC) 设置 10 秒超时。
【解决方案2】:
- (BOOL)isAppConnected
{
    __block BOOL isConnected = NO;

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    // Add this code...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [[SFRestAPI sharedInstance] performSOQLQuery:@"SELECT id FROM Account LIMIT 1"
                                           failBlock:^(NSError *e) {
                                               isConnected = NO;
                                               NSLog(@"NOT CONNECTED %@", e);
                                               NSLog(@"fail block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);

                                               dispatch_semaphore_signal(semaphore);

                                           } completeBlock:^(NSDictionary *dict) {
                                               isConnected = YES;
                                               NSLog(@"%@", dict);
                                               NSLog(@"complete block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);

                                               dispatch_semaphore_signal(semaphore);
                                           }];
    });

    int waitResult = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"waitResult: %d", waitResult);

    return isConnected;
}

【讨论】:

    【解决方案3】:

    首先,如果您的 failcomplete 块在主线程上被调用,那么您将永远等待,或者直到您指定“超时”的超时

    原因是你调用dispatch_semaphore_wait后主线程开始等待。然后,如果您的 performSOQLQuery 调用主线程上的块,则在超时“超时”之前不会发生任何事情。

    如果你指定一个永远的时间,信号量将永远不会发出信号或放开,这意味着你的主线程将永远等待它自己。

    将等待代码改成这样:永远不要让主线程等待

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        int waitResult = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"waitResult: %d", waitResult);
    });
    

    并且不要像你正在做的那样返回 Bool,因为这是一个冗长的操作,你想使用一个有结果的块。

    另外请确保您的 performSOQLQuery() 方法不在主线程上。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-08
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多