主队列上的dispatch_sync在单元测试中挂起

时间:2011-10-19 03:13:06

标签: objective-c grand-central-dispatch sentestingkit

我在使用内置的Xcode单元测试框架SenTestingKit测试一些宏的中央调度代码时遇到了一些麻烦。我成功地解决了我的问题。我有一个单元测试,它构建一个块并尝试在主线程上执行它。但是,该块从未实际执行过,因此测试会挂起,因为它是同步调度。

- (void)testSample {

    dispatch_sync(dispatch_get_main_queue(), ^(void) {
        NSLog(@"on main thread!");
    });

    STFail(@"FAIL!");
}

导致它挂起的测试环境是什么?

5 个答案:

答案 0 :(得分:104)

dispatch_sync在给定队列上运行一个块并等待它完成。在这种情况下,队列是主调度队列。主队列以FIFO(先进先出)顺序在主线程上运行其所有操作。这意味着无论何时调用dispatch_sync,您的新块都将放在该行的末尾,并且在队列中的所有其他内容完成之前不会运行。

这里的问题是你刚入列的块是在等待在主线程上运行的行的 end - 而testSample方法 目前正在主线程上运行。在当前方法(本身)使用主线程完成之前,队列末尾的块无法访问主线程。但是dispatch_sync表示提交一个块对象以便在调度队列上执行,并等待该块完成。

答案 1 :(得分:8)

代码中的问题是,无论您使用dispatch_sync还是dispatch_async,都会始终调用STFail(),导致您的测试失败。

更重要的是,正如BJ Homer所解释的那样,如果你需要在主队列中同步运行某些东西,你必须确保你不在主队列中或者发生死锁。如果您在主队列中,则可以将块作为常规函数运行。

希望这会有所帮助:

- (void)testSample {

    __block BOOL didRunBlock = NO;
    void (^yourBlock)(void) = ^(void) {
        NSLog(@"on main queue!");
        // Probably you want to do more checks here...
        didRunBlock = YES;
    };

    // 2012/12/05 Note: dispatch_get_current_queue() function has been
    // deprecated starting in iOS6 and OSX10.8. Docs clearly state they
    // should be used only for debugging/testing. Luckily this is our case :)
    dispatch_queue_t currentQueue = dispatch_get_current_queue();
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    if (currentQueue == mainQueue) {
        blockInTheMainThread();
    } else {
        dispatch_sync(mainQueue, yourBlock); 
    }

    STAssertEquals(YES, didRunBlock, @"FAIL!");
}

答案 2 :(得分:6)

如果您在主队列中并同步等待主队列可用,您确实会等待很长时间。你应该测试以确保你还没有在主线程上。

答案 3 :(得分:2)

要跟进,因为

dispatch_get_current_queue()

现已弃用,您可以使用

[NSThread isMainThread]

看你是否在主线上。

所以,使用上面的其他答案,你可以这样做:

- (void)testSample 
{
    BOOL __block didRunBlock = NO;
    void (^yourBlock)(void) = ^(void) {
        NSLog(@"on main queue!");
        didRunBlock = YES;
    };

    if ([NSThread isMainThread])
        yourBlock();
    else
        dispatch_sync(dispatch_get_main_queue(), yourBlock);

    STAssertEquals(YES, didRunBlock, @"FAIL!");
}

答案 4 :(得分:2)

如果你必须等待让自己先出屋,你会不会离开家?你猜对了!没有! :

基本上如果:

  1. 您在FooQueue上 。 (不一定是main_queue
  2. 您使用sync调用该方法,即以串行方式调用该方法,并希望在FooQueue上执行
  3. 永远不会出于同样的原因,你永远不会离开家!

    它不会被派遣,因为它必须等待自己离开队列!