dispatch_sync 和 dispatch_async 的串行队列

Serial Queue with dispatch_sync and dispatch_async

以下从主线程调用 [self goToNext] 的代码有不同的结果,<1> 和 <2> 的 dispatch_xxxx 不同。

  1. dispatch_sync & dispatch_sync,结果死锁;
  2. dispatch_async & dispatch_async,结果为NULL;
  3. dispatch_sync & dispatch_async,结果死锁;
  4. dispatch_async & dispatch_sync, 结果为NULL.

    - (NSString *)someString {
        __block NSString *localSomeString;
        dispatch_async(self.serialQueue, ^{     // dispatch_xxx <1>
            if ([NSThread isMainThread]) {
                NSLog(@"main thread");
            } else {
                NSLog(@"background thread");
            }
            localSomeString = @"fuck you!";
        });
        return localSomeString;
    }
    
    - (void)goToNext
    {
        dispatch_sync(self.serialQueue, ^{      // dispatch_xxx <2>
            if ([NSThread isMainThread]) {
                NSLog(@"main thread");
            } else {
                NSLog(@"background thread");
            }
            NSLog(@"%@", [self someString]);
        });
    }
    

    谁能解释一下这四个结果的原因?

在处理给定的代码块之前立即调用 dispatch_async returns。因此,在您的异步示例中,localSomeString 在初始化 (NULL) 之前返回。您可以通过引入一个完成块作为方法 someString 的参数来解决此问题,然后在您的代码块中调用该方法:

- (void)someStringWithCompletion:(void(^)(NSString* someString))completion {
    dispatch_sync<2>(self.serialQueue, ^{
        if ([NSThread isMainThread]) {    
            NSLog(@"main thread");
        } else {
            NSLog(@"background thread");
        }
        localSomeString = @"fuck you to!";
        completion(localSomeString)
    });
}

- (void)goToNext
{
    dispatch_sync<2>(self.serialQueue, ^{
        if ([NSThread isMainThread]) {
            NSLog(@"main thread");
        } else {
            NSLog(@"background thread");
        }
        [self someStringWithCompletion:^(NSString *result) {
            NSLog(result);
        }
    });
}

dispatch_sync 的调用会等待代码块处理完毕,从而阻塞调用它的线程。因此,如果您在当前使用的队列上调用 dispatch_sync,则当前使用的队列会等待代码块完成,但该代码块永远不会执行,因为它位于同一个(当前正在等待的)队列中。

首先:您应该(重新?)阅读 GCD 简介。尝试一些选项直到 运行s 是 no 选项——你显然做了什么——因为这可能会在下一台机器上失败。

您创建了一个串行队列。在串行队列中,一个块一个接一个地执行。这描述了下标块 彼此之间的关系 ,而不是与调用者的关系。

同步或异步订阅描述了块和订阅块的代码之间的关系。这确实 不是 描述块之间的关系,而是与 "caller" 的关系:完全不同的东西。

下一篇:测试一个线程是没有意义的。队列可以更改它使用的线程。这就是他们的目的。

给你的问题:

如果在串行队列里面订阅一个block到串行队列,里面的要等外面的完成,因为是串行队列。这就是串行队列的本质。如果您使用 dispatch_sync() 执行此操作,则调用者会等待,直到块完成。因此它永远不会完成:死锁。我们来简化一下您的代码:

dispatch_sync(self.serialQueue,  // the caller waits for the code to be completed
^{
   …
   dispatch_sync(self.serialQueue, // the outer block waits for the code to be completed
   ^{
    …  // this code runs, after the outer block is completed. 
   });
   …
});

内部块无法完成,因为它必须等待外部块完成(串行队列)。外部块无法完成,因为它等待内部块完成(同步)。死锁:两者都在等待对方完成。

你想要完全不同的东西:你想要使用块的结果。只需将处理结果的代码传递到完成处理程序中。然后你可以 return 立即在完成处理程序中使用结果:

- (void)someStringWithCompletionHandler:(void(^)(NSString *result))handler // No return type, but a completion handler
{
  __block NSString *localSomeString;
  dispatch_async(self.serialQueue, 
  ^{
    if ([NSThread isMainThread]) {
        NSLog(@"main thread");
    } else {
        NSLog(@"background thread");
    }
    localSomeString = @"fuck you!";
    handler( localSomeString );
  });
}

然后这样称呼:

- (void)goToNext
{
  dispatch_async(self.serialQueue, 
  ^{
    [self someStringWithCompletionHandler:
    ^(NSSTring *result)
    {
      NSLog( @"%@", result );
    }]);
  });
}

在 Safari 中输入。

顺便说一句:用注释标记代码中的点,而不是用内联标记。否则none可以复制粘贴代码让它运行.