iOS開發(fā)-多線程-串行、并行、同步、異步

一、一些基礎(chǔ)概念

  1. 進程概念:正在進行中的程序被稱為進程,負責程序運行的內(nèi)存分配,每一個進程都有自己獨立的虛擬內(nèi)存空間。
  2. 線程概念:是進程中的一個獨立執(zhí)行路徑控制單元,一個進程中至少包含一條線程叫做主線程。
  3. 全局隊列概念:是系統(tǒng)開發(fā)的,直接拿過來用,與并行隊列類似,但調(diào)試時無法確認操作所在隊列
  4. 主隊列概念:每一個應(yīng)用程序?qū)?yīng)唯一一個主隊列,直接get使用,在多線程開發(fā)中,使用主隊列更新UI
  • 隊列:隊列負責管理多個任務(wù),隊列擁有一個線程池,池子里有一個或多個線程,它按要求將每個任務(wù)調(diào)度到某一個線程執(zhí)行,說白了隊列就是添加任務(wù),隊列就是往線程中添加任務(wù),注意添加的時機。(串行、并行)一種先進先出的數(shù)據(jù)結(jié)構(gòu),線程的創(chuàng)建和回收不需言程序員操作,由隊列負責。
  • 線程:同步不會創(chuàng)建新的線程,會阻塞當前的線程在這個線程里執(zhí)行任務(wù),異步不會阻塞當前線程,會選擇在恰當?shù)臅r機在當前線程或者另開線程執(zhí)行任務(wù),看系統(tǒng)如何調(diào)度,開始任務(wù)和完成任務(wù)時間是不確定的。(同步、異步)

串行:后一個任務(wù)等待前一個任務(wù)結(jié)束后再執(zhí)行,按添加順序一個個執(zhí)行(A執(zhí)行完才添加B任務(wù))
并行:后一個任務(wù)不會等待前一個任務(wù),不等前一個任務(wù)完成就會分配新任務(wù)(B不用等待A執(zhí)行完再添加任務(wù))隊列是先進先出的,任務(wù)執(zhí)行完畢,不一定出隊列,只有前面的任務(wù)執(zhí)行完了,才會出隊列。
同步:dispatch_sync,同步不會創(chuàng)建新的線程,會阻塞當前的線程在這個線程里執(zhí)行任務(wù)
異步:dispatch_async,異步不會阻塞當前線程,會選擇在恰當?shù)臅r機在當前線程或者另開線程執(zhí)行任務(wù),看系統(tǒng)如何調(diào)度,開始任務(wù)和完成任務(wù)時間是不確定的。

a.串行隊列+同步任務(wù):不會開啟新的線程,任務(wù)逐步完成。
b.并行隊列+同步任務(wù):不會開啟新的線程,任務(wù)逐步完成。
c.串行隊列+異步任務(wù):開啟新的線程,任務(wù)逐步完成。
d.并行隊列+異步任務(wù):開啟新的線程,任務(wù)是同步執(zhí)行的。
隊列的執(zhí)行效果

二、串行、并行、同步、異步的實戰(zhàn)

  1. 拋開同步異步,創(chuàng)建一個隊列
dispatch_queue_t serialQueue = dispatch_queue_create("my.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
/*
dispatch_queue_t:隊列
DISPATCH_QUEUE_SERIAL:串行
DISPATCH_QUEUE_CONCURRENT:并行
*/
  1. 串行、并行、同步、異步的使用事例-------1
viewDidLoad進入時是mainQueue來調(diào)度任務(wù),而mainQueue是串行的(Serial)
a. 串行同步(結(jié)果:1234)
dispatch_queue_t serialQueue = dispatch_queue_create("my.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQueue, ^{
        NSLog(@"1");
        NSLog(@"1sync %@", [NSThread currentThread]);
    });
    NSLog(@"2");
    dispatch_sync(serialQueue, ^{
        NSLog(@"3");
        NSLog(@"3sync %@", [NSThread currentThread]);
    });
    NSLog(@"4");
b. 并行同步(結(jié)果:1234)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("my.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(conCurrentQueue, ^{
        NSLog(@"1");
        NSLog(@"1sync %@", [NSThread currentThread]);
    });
    NSLog(@"2");
    dispatch_sync(conCurrentQueue, ^{
        NSLog(@"3");
        NSLog(@"3sync %@", [NSThread currentThread]);
    });
    NSLog(@"4");
c. 串行異步(結(jié)果:2413、2143、1234,只有一個子線程哦,因為是串行)
dispatch_queue_t serialQueue = dispatch_queue_create("my.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{
        NSLog(@"1");
        NSLog(@"1sync %@", [NSThread currentThread]);
    });
    NSLog(@"2");
    dispatch_async(serialQueue, ^{
        NSLog(@"3");
        NSLog(@"3sync %@", [NSThread currentThread]);
    });
    NSLog(@"4");
結(jié)果分析:因為是異步,所以24在主線程中,13在子線程中,又因為是串行,所以13和24順序定了,但又因為是異步,完成時間不確定,所以會出現(xiàn)多種情況,2和4處于主線程串行隊列哦~切記
d.并行異步(結(jié)果:24順序一定,13順序不一定,而且24處于處于主線程,13處于不同的子線程)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("my.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(conCurrentQueue, ^{
        NSLog(@"1");
        NSLog(@"1sync %@", [NSThread currentThread]);
    });
    NSLog(@"2");
    dispatch_async(conCurrentQueue, ^{
        NSLog(@"3");
        NSLog(@"3sync %@", [NSThread currentThread]);
    });
    NSLog(@"4");
  1. 串行、并行、同步、異步的使用事例-------2
a.串行同步(結(jié)果:開始123~9結(jié)束)
-(void)test1
{
    dispatch_queue_t q = dispatch_queue_create("fs", DISPATCH_QUEUE_SERIAL);
    NSLog(@"開始:%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        if (i % 2 == 0) {
            [NSThread sleepForTimeInterval:1];
        } else {
            [NSThread sleepForTimeInterval:2];
        }
        dispatch_sync(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"結(jié)束:%@",[NSThread currentThread]);
}
b.并行同步(結(jié)果:開始123~9結(jié)束)
-(void)test2
{
    dispatch_queue_t q = dispatch_queue_create("fs", DISPATCH_QUEUE_SERIAL);
    NSLog(@"開始:%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_sync(q, ^{
            if (i % 2 == 0) {
                [NSThread sleepForTimeInterval:1];
            } else {
                [NSThread sleepForTimeInterval:2];
            }
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }    NSLog(@"結(jié)束:%@",[NSThread currentThread]);
}
c.串行異步(結(jié)果:開始<結(jié)束不確定位置>123~9)
- (void)test3
{
    dispatch_queue_t serialQueue = dispatch_queue_create("my.serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"開始:%@", [NSThread currentThread]);
    for (int i = 0; i < 10; i++) {
        dispatch_async(serialQueue, ^{
            if (i % 2 == 0) {
                [NSThread sleepForTimeInterval:1];
            } else {
                [NSThread sleepForTimeInterval:2];
            }
            NSLog(@"異步:%@------i:%d", [NSThread currentThread], i);
        });
    }
    NSLog(@"結(jié)束:%@", [NSThread currentThread]);
}
d.并行異步(結(jié)果:開始<1~9不確定,結(jié)束不確定>)
-(void)test4
{
    dispatch_queue_t q = dispatch_queue_create("fs", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"開始:%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            if (i % 2 == 0) {
                [NSThread sleepForTimeInterval:1];
            } else {
                [NSThread sleepForTimeInterval:2];
            }
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    [NSThread sleepForTimeInterval:5];
    NSLog(@"結(jié)束:%@",[NSThread currentThread]);
}
  1. 主線程死鎖情況
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^ {
  NSLog(@"2");
});
NSLog(@"3");

三、串行、并行、同步、異步的總結(jié)

1.不要向同一個串行隊列添加同步任務(wù),進行中的任務(wù)等待添加任務(wù)完成,添加的任務(wù)等待上一個任務(wù)完成,因為相互等待死循環(huán),在currentQueue中謹慎使用dispatch_sync

  1. 你需要知道的一些結(jié)論

1.同步、異步?jīng)Q定是否創(chuàng)建子線程,同步任務(wù)不創(chuàng)建子線程,都是在主線程中執(zhí)行,異步任務(wù)創(chuàng)建子線程
2.串行、并行決定創(chuàng)建子線程的個數(shù),串行創(chuàng)建一個子線程,并行創(chuàng)建多個子線程
3.同步會阻塞線程,異步是開啟另一個線程來執(zhí)行,開啟的這個是子線程。異步的子線程會在后臺跑起來,甚至超過了主線程的速度,但是關(guān)于刷新UI的事情一定要回歸主線程來執(zhí)行。子線程不具備刷新UI的功能??梢愿碌慕Y(jié)果只是一個幻像:因為子線程代碼執(zhí)行完畢了,又自動進入到了主線程,執(zhí)行了子線程中的UI更新的函數(shù)棧。
4.主線程中不能使用同步。會發(fā)生循環(huán)等待(主線程等待該線程執(zhí)行完畢,該線程需要調(diào)用主線程執(zhí)行)。
5.串行隊列同步:操作不會新建線程、操作順序執(zhí)行;
6.串行隊列異步:操作需要一個子線程,會新建線程、線程的創(chuàng)建和回收不需要程序員參與,操作順序執(zhí)行,是最安全的選擇;
7.并行隊列同步:操作不會新建線程、操作順序執(zhí)行;
8.并行隊列異步:操作會新建多個線程(有多少任務(wù),就開n個線程執(zhí)行)、操作無序執(zhí)行;隊列前如果有其他任務(wù),會等待前面的任務(wù)完成之后再執(zhí)行;場景:既不影響主線程,又不需要順序執(zhí)行的操作!
9.全局隊列異步:操作會新建多個線程、操作無序執(zhí)行,隊列前如果有其他任務(wù),會等待前面的任務(wù)完成之后再執(zhí)行;
10.全局隊列同步:操作不會新建線程、操作順序執(zhí)行;
11.主隊列異步:操作都應(yīng)該在主線程上順序執(zhí)行的,不存在異步的;
12.主隊列同步:如果把主線程中的操作看成一個大的block,那么除非主線程被用戶殺掉,否則永遠不會結(jié)束;主隊列中添加的同步操作永遠不會被執(zhí)行,會死鎖;
  1. 深入

1.全局隊列,都在主線程上執(zhí)行,不會死鎖 dispatch_queue_priority_default
dispatch_queue_t q = dispatch_get_global_queue(dispatch_queue_priority_default, 0);
2.并行隊列,都在主線程上執(zhí)行,不會死鎖 dispatch_queue_concurrent
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", dispatch_queue_concurrent);
3.串行隊列,會死鎖,但是會執(zhí)行嵌套同步操作之前的代碼dispatch_queue_serial
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", dispatch_queue_serial);
4.主隊列,直接死鎖 dispatch_get_main_queue();
dispatch_queue_t q = dispatch_get_main_queue();
  1. dispatch_sync同步應(yīng)用場景
    阻塞并行隊列的執(zhí)行,要求某一操作執(zhí)行后在進行后續(xù)操作,如登陸,確保代碼塊之外的局部變量確實被修改
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", dispatch_queue_concurrent);
  __block BOOL login = no;
  dispatch_sync(q, ^{
    NSLog(@"模擬耗時操作 %@", [NSThread currentthread]);
    [NSThread sleepfortimeinterval:2.0f];
    NSLog(@"模擬耗時完成 %@", [NSThread currentthread]);
    login = yes; 
 });
dispatch_async(q, ^{
  Nslog(@"登錄完成的處理 %@", [nsthread currentthread]);
});
  1. 主線程隊列和GCD創(chuàng)建的隊列區(qū)別
    主線程隊列和GCD創(chuàng)建的隊列是不同的,GCD創(chuàng)建的隊列優(yōu)先級沒有主隊列高,所以在GCD中的串行隊列開啟同步任務(wù)中沒有嵌套任務(wù)是不會阻塞主線程,只有一種可能導(dǎo)致死鎖,就是串行隊列嵌套開啟任務(wù)有可能導(dǎo)致死鎖。
    主線程隊列中不能開啟同步,會阻塞主線程。只能開啟異步任務(wù),開啟異步任務(wù)也不會開啟新的線程,只是降低異步任務(wù)的優(yōu)先級,讓cpu空閑的時候才去調(diào)用。而同步任務(wù),會搶占主線程的資源,會造成死鎖。
  2. 兩個問題

1.在主隊列開啟異步任務(wù),不會開啟新的線程而是依然在主線程中執(zhí)行代碼塊中的代碼。為什么不會阻塞線程?
答:主隊列開啟異步任務(wù),雖然不會開啟新的線程,但是他會把異步任務(wù)降低優(yōu)先級,等閑著的時候,就會在主線程上執(zhí)行異步任務(wù)。
2.在主隊列開啟同步任務(wù),為什么會阻塞線程?
答:在主隊列開啟同步任務(wù),因為主隊列是串行隊列,里面的線程是有順序的,先執(zhí)行完一個線程才執(zhí)行下一個線程,而主隊列始終就只有一個主線程,主線程是不會執(zhí)行完畢的,因為他是無限循環(huán)的,除非關(guān)閉應(yīng)用程序。因此在主線程開啟一個同步任務(wù),同步任務(wù)會想搶占執(zhí)行的資源,而主線程任務(wù)一直在執(zhí)行某些操作,不肯放手。兩個的優(yōu)先級都很高,最終導(dǎo)致死鎖,阻塞線程了。

四、NSThread、NSOperation、GCD

1. NSThread

a.使用NSThread對象建立一個線程非常方便
b.使用NSThread管理多個線程非常麻煩
c.使用[NSThread currentthread]跟蹤任務(wù)所在線程

2. NSOperation

a.是使用GCD實現(xiàn)的一套Objective-c的API
b.是面向?qū)ο蟮木€程技術(shù)
c.提供了一些在GCD中不容易實現(xiàn)的特性(限制最大并發(fā)數(shù),操作之間的依賴關(guān)系)

3. GCD

a.基于C語言的底層API
b.用Block定義任務(wù),使用起來非常靈活便捷
c.提供了更多的控制能力以及操作隊列中所不能使用的底層函數(shù)

參考文章:
iOS 多線程 同步和異步 串行和并行
GCD編程中串行、并行、同步、異步的執(zhí)行順序
iOS 同步異步。串行并行的區(qū)別
IOS多線程知識總結(jié)/隊列概念/GCD/串行/并行/同步/異步

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • iOS多線程編程 基本知識 1. 進程(process) 進程是指在系統(tǒng)中正在運行的一個應(yīng)用程序,就是一段程序的執(zhí)...
    陵無山閱讀 6,335評論 1 14
  • 從哪說起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個最簡單的問題,以這個作為切入點好了 在ma...
    Mr_Baymax閱讀 2,902評論 1 17
  • 一.概述 1.基本概念 同步與異步的概念 同步 必須等待當前語句執(zhí)行完畢,才可以執(zhí)行下一個語句。 異步 不用等待當...
    Jt_Self閱讀 536評論 0 1
  • 或許,你現(xiàn)在正承受著被無能所支配的恐慌;前路茫茫,竟無自己所出之路,就好像在一個寒夜里,蜷縮在冰冷的床上,...
    啵啵愛吃魚閱讀 688評論 2 6
  • 今天晚上,第一個班下課后,帶著孩子到了雙子座上第二堂課。 我在一間教室上課,她在另一間教室寫作業(yè)。她不需...
    莊如澖閱讀 259評論 0 0

友情鏈接更多精彩內(nèi)容