1. 程式人生 > >GCD呼叫 引發的一些思考

GCD呼叫 引發的一些思考

前兩天同事,提了個有點意思的問題,今天突然想起來就整理一下,順便談一下自己的理解。

下面的這段程式碼會怎麼列印?

Dog * dog = [Dog new];

   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{

        dispatch_async(dispatch_get_main_queue(), ^{

            if(dog.run){

                dog.run();

                NSLog(@"開始跑");

            }else{

                NSLog(@"跑不了");

            }

        });

    });



    sleep(5);

    [dog setRun:^{

        NSLog(@"可以跑");

    }];

程式碼解釋:

  • 第一步:建立一個 名為 dog的物件。

  • 第二步:在GCD的延時函式中,非同步到主佇列判斷是否可以呼叫dog的run閉包,並進行相關處理。

  • 第三步:呼叫sleep函式 讓執行緒暫停5秒。

  • 第四步: dog的run閉包的實現,並在實現中列印訊息。

  • 最後的輸出結果是:
    在這裡插入圖片描述

為什麼會是這樣的輸出或者執行順序呢? 思考這個問題,我們不妨可以考慮一下幾種情況:

第一種:

延時函式,改為在main queue中呼叫。

在這裡插入圖片描述

第二種情況:

在延時函式的呼叫中,把非同步改為同步。

在這裡插入圖片描述

第三種情況:

我們把sleep函式的呼叫 註釋掉。

在這裡插入圖片描述

在以上三種的列印中,會是怎麼列印的?

其實列印的結果都是一樣的。

為什麼呢?

我們可以看一下 "簡化後"的程式碼:

在這裡插入圖片描述

上面的程式碼,我想應該是很容易理解的 首先執行非同步操作到主佇列中去提交任務,然後執行 dog 閉包的實現,最後的輸出結果是:

先列印: 可以跑

然後列印:開始跑。

這段程式碼的執行,我們可以這麼理解:

  • 1、在主佇列中,執行非同步到主佇列 將 dog物件的閉包呼叫 提交到主佇列中去執行。

  • 2、緊接著 dog物件的run閉包的實現。

執行的順序我們可以這麼理解:

在這裡插入圖片描述

如上圖所示:

也就是說首先執行到 dispatch_async這個函式, 因為它是非同步的 不會卡死當前的執行緒,所以main queue 或者說 主執行緒 會繼續往下執行,也就是會執行到 dog物件的實現。而非同步執行的任務是提交到 主佇列中的,我們知道主佇列是不具有併發執行的能力的。 所以此時,執行到 呼叫dog物件的run閉包,可以正常呼叫。

如此,最開始的問題,我們也就水到渠成可以理解了。

在上面提及的幾種情況,其執行的主要步驟機制便是如此,所以結果也是如此。

提出問題的情況中我們需要注意一下幾點:

  • 1、雖然延時函式的呼叫並沒有什麼意義,但是我們需要注意延時函式執行的佇列,然後在延時函式的呼叫中,考慮使用 同步還是非同步,避免死鎖的問題。

  • 2、我們常說的非同步執行或者同步執行 需要注意的是都是相對於當前的執行緒,但是具體會不會開闢新的執行緒去執行任務,還要看所處於的執行的佇列。

  • 3、sleep函式是誰呼叫,誰就會去睡覺,而且sleep並不會讓出所使用的系統資源,除非強行interrupt.

  • 4、看問題,我們要儘量去掉 一些天花亂墜的掩飾 (如上面的 延時呼叫、執行緒休眠等),只有直視問題本身,才會得到最真實的答案。