Objective c 具有调度同步和调度异步的串行队列

Objective c 具有调度同步和调度异步的串行队列,objective-c,grand-central-dispatch,Objective C,Grand Central Dispatch,以下从主线程调用[self goToNext]的代码对于和具有不同的调度_xxxx具有不同的结果 dispatch\u sync&dispatch\u sync,结果为死锁 dispatch\u async&dispatch\u async,结果为空 dispatch\u sync&dispatch\u async,结果是死锁 dispatch\u async&dispatch\u sync,结果为空 - (NSString *)someString { __block NSString

以下从主线程调用[self goToNext]的代码对于和具有不同的调度_xxxx具有不同的结果

  • dispatch\u sync&dispatch\u sync,结果为死锁
  • dispatch\u async&dispatch\u async,结果为空
  • dispatch\u sync&dispatch\u async,结果是死锁
  • dispatch\u async&dispatch\u sync,结果为空

    - (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]);
        });
    }
    
    -(NSString*)someString{
    __块NSString*localSomeString;
    dispatch\u async(self.serialQueue,^{//dispatch\u xxx
    如果([NSThread isMainThread]){
    NSLog(@“主线程”);
    }否则{
    NSLog(@“后台线程”);
    }
    localSomeString=@“操你妈的!”;
    });
    返回localSomeString;
    }
    -(作废)GotonNext
    {
    调度同步(self.serialQueue,^{//dispatch\u xxx
    如果([NSThread isMainThread]){
    NSLog(@“主线程”);
    }否则{
    NSLog(@“后台线程”);
    }
    NSLog(@“%@,[self someString]);
    });
    }
    
    有人能解释这四个结果的原因吗


  • 在处理给定的代码块之前,对
    dispatch\u async
    的调用会立即返回。因此,在异步示例中,
    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);
            }
        });
    }
    
    -(void)someStringWithCompletion:(void(^)(NSString*someString))completion{
    调度同步(self.serialQueue^{
    如果([NSThread isMainThread]){
    NSLog(@“主线程”);
    }否则{
    NSLog(@“后台线程”);
    }
    localSomeString=@“去你的!”;
    完成(localSomeString)
    });
    }
    -(作废)GotonNext
    {
    调度同步(self.serialQueue^{
    如果([NSThread isMainThread]){
    NSLog(@“主线程”);
    }否则{
    NSLog(@“后台线程”);
    }
    [self someStringWithCompletion:^(NSString*结果){
    NSLog(结果);
    }
    });
    }
    
    dispatch\u sync
    的调用将等待代码块被处理,从而阻止调用它的线程。因此,如果在当前使用的队列上调用
    dispatch\u sync
    ,则当前使用的队列将等待代码块完成,但代码块永远不会像在同一队列上一样执行(当前正在等待)排队。

    首先:您应该(重新?)阅读GCD的介绍。尝试一些选项直到其中一个运行是没有选择的,您显然是这样做的,因为这可能会在下一台机器上失败

    创建一个串行队列。在串行队列中,一个块依次执行。这描述了订阅块之间的关系,而不是与调用者之间的关系

    同步或异步订阅描述块与订阅块的代码之间的关系。这并不是描述块之间的关系,而是描述块与“调用者”之间的关系:完全不同的东西

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

    对你的问题:

    如果在串行队列中向串行队列订阅块,则内部块必须等待外部块完成,因为它是串行队列。这是串行队列的性质。如果使用
    dispatch\u 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. 
       });
       …
    });
    
    内部块无法完成,因为它必须等待外部块完成(串行队列)。外部块无法完成,因为它等待内部块完成(同步)。死锁:两者都在等待另一个块完成

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

    - (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


    顺便说一句:在代码中用注释标记点,而不是内联标记。否则,没有人可以复制和粘贴代码并让它运行。

    我从dispatch_sync文档“调用此函数并针对当前队列导致死锁”中知道第一个原因。但我不理解其余三种情况。