簡體   English   中英

dispatch_async(dispatch_get_main_queue(),^ {...});等到完成?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM