在 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
在主队列上调度,第二个将始终如此。
如有不明之处欢迎随时提问
比方说,我有一个名为 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
在主队列上调度,第二个将始终如此。
如有不明之处欢迎随时提问