在 dispatch_async 和 dispatch_sysnc 中更新 UI 时的差异

difference when updating UI in dispatch_async and dispatch_sysnc

比方说,我有一个名为 session 的 NSURLSession,我想在 downloadTaskWithRequest 中更新我的 UI。现在,情况 1 和情况 2 会发生什么:

案例一:(dispatch_async)

NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

NSURLSessionDownloadTask *task= [session downloadTaskWithRequest:request
completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error){
    dispatch_async(dispatch_get_main_queue(), ^{
                    // UI Update    
                                               });
}];

案例 2:(使用 dispatch_sync)

NSURLSessionDownloadTask *task= [session downloadTaskWithRequest:request
completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error){
    dispatch_sync(dispatch_get_main_queue(), ^{
                    // UI Update    
                                               });
}];

请注意 dispatch_get_main_queue() is a serial queue

因此,无论何时分派要在其上执行的块,它都会到达行尾,并在处理器按顺序执行完它前面的所有其他任务后执行。

当你 dispatch_async 一个块(任务)时,就会发生这种情况:任务将放在行尾,dispatch_async 之后的代码将继续执行。一旦主线程完成该分派任务前面的所有任务(运行 循环结束),您的任务将执行

然而,当您 dispatch_sync 时,当前线程(在您的情况下是主线程)将被阻塞,因为它等待分派的任务执行。正如@dan 在评论中提到的,因为你当前在主线程中,并且你 dispatch_sync 一个任务到主线程,你会死锁。这是因为您在主线程的末尾添加了一个任务,而主线程中位于行首的任务正在等待最后一个任务执行——但它永远不会执行,因为它在排队。

因此,如果您想在单独的线程上执行任务,但不希望它异步发生,您可以使用 dispatch_sync。只要确保你永远不会分派到与当前线程相同的线程

tl;dr 第二种情况将等待 UI 更新完成。第一个更快。

去尾

dispathc_sync 阻止在当前队列上执行,直到分派的任务完成。所以,如果主队列发生了大事,情况二将需要很长时间才能完成。但是,当完成处理程序完成其工作时(例如,如果您在 UI 更新调度之后执行某些操作),您确定 UI 已经更新。 此外,如果出于某种未知原因,您将配置 session 以在主队列上分派完成块,则在第二种情况下您将陷入僵局。这是因为主队列是串行的,这意味着它一次只执行一个任务 - 所以完成处理程序将等待 UI 更新完成,并且 UI 更新不会开始直到完成处理程序没有完成.

dispatch_async 不会阻塞执行流程。这意味着,您不会以任何方式陷入僵局,并且完成块执行时间将不取决于 UI 更新持续时间。但是,即使完成处理程序已完成,您也不知道 UI 更新是否已完成。 但是,如果您的 completionHandler 分派到主队列,则 UI 更新将仅在 completionHandler 完成后执行。

细分 案例二:

completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error){
    //Some code
    //1
    dispatch_sync(dispatch_get_main_queue(), ^{
                    //2
                    // UI Update    
                    //3
                                               });
    //Some code
    //4
}];

达到 //1,达到 //2,达到 //3,达到 //4。保证执行顺序。无论如何 - 无论是这样还是根本不执行。

案例一:

completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error){
    //Some code
    //1
    dispatch_async(dispatch_get_main_queue(), ^{
                    //2
                    // UI Update    
                    //3
                                               });
    //Some code
    //4
}];

达到 //1。保证。在那之后变种可能。 达到 //2,达到 //4,达到 //3。 要么 达到 //4,达到 //2,达到 //3 要么 达到 //2,达到 //3,达到 //4

如果 completionBlock 在主队列上调度,第二个将始终如此。

如有不明之处欢迎随时提问