iOS多線程中,隊列和執行的排列組合結果分析


本文是對以往學習的多線程中知識點的一個整理。

多線程中的隊列有:串行隊列,並發隊列,全局隊列,主隊列

執行的方法有:同步執行和異步執行。那么兩兩一組合會有哪些注意事項呢?

如果不是在董鉑然博客園看到這邊文章請 點擊查看原文

提到多線程,也就是四種,pthread,NSthread,GCD,NSOperation  

其中phtread是跨平台的。GCD和NSOperation都是常用的,后者是基於前者的。

但是兩者區別:GCD的核心概念是將一個任務添加到隊列,指定任務執行的方法,然后執行。 NSOperation則是直接將一個操作添加到隊列中。

為了整體結構更加清晰,我是用GCD來做此排列組合的實驗。實驗主要是通過循環內打印和主線程的打印先后順序來判斷結果,最后再加以總結

1.串行隊列,同步執行

    dispatch_queue_t q = dispatch_queue_create("dantesx", NULL);
    
    // 執行任務
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"董鉑然 come here");

運行效果:

 

執行結果可以清楚的看到全在主線程執行,並且是按照數序執行,循環結束之后主線程的打印才輸出。

2.串行隊列,異步執行

    dispatch_queue_t q = dispatch_queue_create("dantesx", NULL);
    
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    //    [NSThread sleepForTimeInterval:0.001];
    NSLog(@"董鉑然 come here");

 運行結果

結果顯示,系統開了1條異步線程,因此全部在線程2執行,並且是順序執行。主線程打印雖然在最上面,但是這個先后順序是不確定,如果睡個0.001秒,主線程的打印會混在中間。

3.並發隊列,異步執行

    // 1. 隊列
    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 異步執行
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    //    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"董鉑然 come here");

 運行結果

結果顯示,主線程的打印還是混在中間不確定的,因為異步線程就是誰也不等誰。系統開了多條線程,並且執行的順序也是亂序的

 

4.並發隊列,同步執行

    // 1. 隊列
    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 同步執行
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    //    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"董鉑然 come here");

 運行結果

這個運行結果和第1種的串行隊列,同步執行是一模一樣的。 因為同步任務的概念就是按順序執行,后面都要等。言外之意就是不允許多開線程。 同步和異步則是決定開一條還是開多條。

所以一旦是同步執行,前面什么隊列已經沒區別了

5.主隊列,異步執行

    // 1. 主隊列 - 程序啟動之后已經存在主線程,主隊列同樣存在
    dispatch_queue_t q = dispatch_get_main_queue();
    // 2. 安排一個任務
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"睡會");
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"董鉑然 come here");

 運行結果

結果顯示有點出人意料。主線程在睡會之后才打印,循環一直在等着。因為主隊列的任務雖然會加到主線程中執行,但是如果主線程里也有任務就必須等主線程任務執行完才輪到主隊列的。

6.主隊列,同步執行

    dispatch_queue_t q = dispatch_get_main_queue();
    
    NSLog(@"卡死了嗎?");
    
    dispatch_sync(q, ^{
        NSLog(@"我來了");
    });
    
    NSLog(@"董鉑然 come here");

 運行結果為卡死

卡死的原因是循環等待,主隊列的東西要等主線程執行完,而因為是同步執行不能開線程,所以下面的任務要等上面的任務執行完,所以卡死。這是排列組合中唯一一個會卡死的組合。

7.同步任務的使用場景

    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    // 1. 用戶登錄,必須要第一個執行
    dispatch_sync(q, ^{
        [NSThread sleepForTimeInterval:2.0];
        NSLog(@"用戶登錄 %@", [NSThread currentThread]);
    });
    // 2. 扣費
    dispatch_async(q, ^{
        NSLog(@"扣費 %@", [NSThread currentThread]);
    });
    // 3. 下載
    dispatch_async(q, ^{
        NSLog(@"下載 %@", [NSThread currentThread]);
    });
    NSLog(@"董鉑然 come here");

 運行結果

結果顯示,“用戶登陸”在主線程打印,后兩個在異步線程打印。上面的“用戶登陸”使用同步執行,后面的扣費和下載都是異步執行。所以“用戶登陸”必須第一個打印出來不管等多久,然后后面的兩個異步和主線程打印會不確定順序的打印。這就是日常開發中,那些后面對其有依賴的必須要先執行的任務使用同步執行,然后反正都要執行先后順序無所謂的使用異步執行。

8.block異步任務包裹同步任務

    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    void (^task)() = ^ {
        // 1. 用戶登錄,必須要第一個執行
        dispatch_sync(q, ^{
            NSLog(@"用戶登錄 %@", [NSThread currentThread]);
        });
        // 2. 扣費
        dispatch_async(q, ^{
            NSLog(@"扣費 %@", [NSThread currentThread]);
        });
        // 3. 下載
        dispatch_async(q, ^{
            
            NSLog(@"下載 %@", [NSThread currentThread]);
        });
    };
    
    dispatch_async(q, task);
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"董鉑然 come here");

 運行結果

因為整個block是在異步執行的,所以即使里面“用戶登陸”是同步執行,那也無法在主線程中執行,只能開一條異步線程執行,因為是同步的所以必須等他先執行,后面的“扣費”和“下載”在上面同步執行結束之后,不確定順序的打印。

9.全局隊列

    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"com here");

 運行結果

全局隊列的本質就是並發隊列,只是在后面加入了,“服務質量”,和“調度優先級” 兩個參數,這兩個參數一般為了系統間的適配,最好直接填0和0

如果不是在董鉑然博客園看到這邊文章請 點擊查看原文

總結:

1. 開不開線程,取決於執行任務的函數,同步不開,異步開。

2. 開幾條線程,取決於隊列,串行開一條,並發開多條(異步)

3. 主隊列:  專門用來在主線程上調度任務的"隊列",主隊列不能在其他線程中調度任務!

4. 如果主線程上當前正在有執行的任務,主隊列暫時不會調度任務的執行!主隊列同步任務,會造成死鎖。原因是循環等待

5. 同步任務可以隊列調度多個異步任務前,指定一個同步任務,讓所有的異步任務,等待同步任務執行完成,這是依賴關系。

6. 全局隊列:並發,能夠調度多個線程,執行效率高,但是相對費電。 串行隊列效率較低,省電省流量,或者是任務之間需要依賴也可以使用串行隊列。

7. 也可以通過判斷當前用戶的網絡環境來決定開的線程數。WIFI下6條,3G/4G下2~3條。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM