[英]Under what circumstances does dispatch_async(dispatch_get_main_queue(), ^ … do nothing
[英]Does dispatch_async(dispatch_get_main_queue(), ^{…}); wait until done?
我的應用程序中有一個場景,我想在一個方法中做一些耗費時間的任務,其中包括一些數據處理和UI更新。 我的方法看起來像這樣,
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
// UI UPDATE 1
// DATA PROCESSING 2
// UI UPDATE 2
// DATA PROCESSING 3
// UI UPDATE 3
}
由於耗時我想在后台線程上進行數據處理,使用,
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
但由於數據處理和UI更新都采用相同的方法,我只想在主線程中使用UI更新,
dispatch_async(dispatch_get_main_queue(), ^{
最后我的方法看起來像這樣,
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
}
這真的有用嗎? 這真的是一個好習慣嗎? 實現這一目標的最佳方法是什么?
PS所有這三個操作都是相互關聯的。
編輯:對不起伙計們。 我錯過了上面代碼中的一行 。 我的實際代碼看起來像這樣。
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
});
}
再一次,我真的為這種混亂道歉。
不,它不等待,你在那個樣本中做的方式不是好習慣。
dispatch_async
始終是異步的 。 只是您將所有UI塊排入同一隊列,這樣不同的塊將按順序運行,但與您的數據處理代碼並行。
如果您希望更新等待,則可以使用dispatch_sync
。
// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
另一種方法是嵌套排隊。 我不建議將它用於多個級別。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
});
});
});
});
如果您需要更新UI以等待,那么您應該使用同步版本。 有一個后台線程等待主線程是完全可以的。 UI更新應該非常快。
您必須將主隊列調度放在運行計算的塊中。 例如(這里我創建一個調度隊列,不使用全局隊列):
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
當然,如果你創建一個隊列,如果你的目標是6.0之前的iOS版本,請不要忘記dispatch_release
。
您提議的doCalculationsAndUpdateUIs
執行數據處理並將UI更新分派給主隊列。 我假設您在第一次調用doCalculationsAndUpdateUIs
調度到后台隊列。
雖然技術上很好,但這有點脆弱,取決於你每次打電話時都記得將它發送到后台:相反,我會建議你把你的調度發送到后台並從同一時間發送回主隊列。方法,因為它使邏輯明確,更健壯,等等。
因此它可能看起來像:
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 1
});
/* I expect the control to come here after UI UPDATION 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 2
});
/* I expect the control to come here after UI UPDATION 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 3
});
});
}
在你是否異步派遣你的UI更新的條款dispatch_async
(其中后台進程將不會等待UI更新)或同步於dispatch_sync
(在那里將等待UI更新),問題是,為什么你會想這樣做同步:您是否真的想在等待UI更新時減慢后台進程,或者您希望在UI更新發生時繼續進行后台進程。
一般來說,你將與異步分派UI更新dispatch_async
正如您在你原來的問題使用。 是的,確實存在需要同步調度代碼的特殊情況(例如,您通過在主隊列上對其執行所有更新來將更新同步到某個類屬性),但通常情況下,您只需調度UI更新異步並繼續。 同步調度代碼可能會導致問題(例如死鎖),所以我的總法律顧問是,如果有一些迫切的需要,你應該只能同步調度UI更新,否則你應該設計你的解決方案,這樣你就可以異步調度它們。
在回答您關於這是否是“實現這一目標的最佳方式”的問題時,我們很難在不了解更多有關正在解決的業務問題的情況下說出來。 例如,如果您可能多次調用此doCalculationsAndUpdateUIs
,我可能傾向於使用我自己的串行隊列而不是並發全局隊列,以確保它們不會相互跳過。 或者,如果您可能需要能夠在用戶解除場景或再次調用該方法時取消此doCalculationsAndUpdateUIs
,那么我可能傾向於使用提供取消功能的操作隊列。 這完全取決於你想要實現的目標。
但是,通常,將復雜任務異步調度到后台隊列然后異步調度UI更新回主隊列的模式非常常見。
如果要運行單個獨立的排隊操作而不關心其他並發操作,可以使用全局並發隊列:
dispatch_queue_t globalConcurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
這將返回具有給定優先級的並發隊列,如文檔中所述:
DISPATCH_QUEUE_PRIORITY_HIGH分派給隊列的項目將以高優先級運行,即隊列將被調度為在任何默認優先級或低優先級隊列之前執行。
DISPATCH_QUEUE_PRIORITY_DEFAULT調度到隊列的項目將以默認優先級運行,即在調度所有高優先級隊列之后但在調度任何低優先級隊列之前,將調度隊列執行。
DISPATCH_QUEUE_PRIORITY_LOW調度到隊列的項目將以低優先級運行,即在調度所有默認優先級和高優先級隊列之后,將調度隊列以執行。
DISPATCH_QUEUE_PRIORITY_BACKGROUND調度到隊列的項目將以后台優先級運行,即在調度所有優先級較高的隊列后,隊列將被調度執行,系統將根據setpriority(2)在具有后台狀態的線程上運行此隊列中的項目(2)(即磁盤I / O被限制,線程的調度優先級設置為最低值。
不,它不會等待。
你可以使用performSelectorOnMainThread:withObject:waitUntilDone:
.
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
好的做法是: 派遣小組
dispatch_group_t imageGroup = dispatch_group_create();
dispatch_group_enter(imageGroup);
[uploadImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image successfully uploaded to S3
dispatch_group_leave(imageGroup);
}];
dispatch_group_enter(imageGroup);
[setImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image url updated
dispatch_group_leave(imageGroup);
}];
dispatch_group_notify(imageGroup,dispatch_get_main_queue(),^{
// We get here when both tasks are completed
});
好的,有兩種方法可以做到這一點:
// GLOBAL_CONCURRENT_QUEUE
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE
{
dispatch_queue_t globalConcurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalConcurrentQ, ^{
// DATA PROCESSING 1
sleep(1);
NSLog(@"Hello world chekpoint 1");
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 1
sleep(1);
NSLog(@"Hello world chekpoint 2");
});
/* the control to come here after UI UPDATION 1 */
sleep(1);
NSLog(@"Hello world chekpoint 3");
// DATA PROCESSING 2
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 2
sleep(1);
NSLog(@"Hello world chekpoint 4");
});
/* the control to come here after UI UPDATION 2 */
sleep(1);
NSLog(@"Hello world chekpoint 5");
// DATA PROCESSING 3
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 3
sleep(1);
NSLog(@"Hello world chekpoint 6");
});
});
}
// SERIAL QUEUE
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE
{
dispatch_queue_t serialQ = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(serialQ, ^{
// DATA PROCESSING 1
sleep(1);
NSLog(@"Hello world chekpoint 1");
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 1
sleep(1);
NSLog(@"Hello world chekpoint 2");
});
sleep(1);
NSLog(@"Hello world chekpoint 3");
// DATA PROCESSING 2
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 2
sleep(1);
NSLog(@"Hello world chekpoint 4");
});
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.