iOS開發(fā)進階-多線程技術(shù)

多線程

iOS中多線程

首先看一道面試題

iOS中多線程有哪些實現(xiàn)方案?

技術(shù)方案 簡介 語言 線程聲明周期 使用頻率
pthread 1. 一套通用的多線程API 2. 跨平臺/可移植 3. 使用難度大 C 程序員管理 幾乎不用
NSThread 1.面向?qū)ο?2.簡單易用直接操作線程對象 OC 程序員管理 偶爾使用
GCD 1.旨在替代NSThread等線程技術(shù) 2.充分利用設(shè)備的多核 C 自動管理 經(jīng)常使用
NSOperation 1.基于GCD 2.比GCD多了一些更實用的函數(shù) OC 自動管理 經(jīng)常使用

iOS中,多線程一般有三種方案GCD涯保、NSOperationNSThread

這一篇文章周伦,會學習iOS中關(guān)于多線程相關(guān)的問題夕春,以及面試中的問題。

一专挪、GCD

GCD相關(guān)問題一般分為三個方面:首先及志,同步/異步串行/并發(fā)問題;其次狈蚤,dispatch_barrier_async異步柵欄調(diào)用困肩,解決多讀單寫問題;最后脆侮,dispatch_group使用和理解锌畸。

GCD中有2中用來執(zhí)行任務的函數(shù):同步和異步;同時還有兩種類型的隊列:并發(fā)和串行隊列靖避。并發(fā)隊列讓多個任務并發(fā)執(zhí)行潭枣,自動開啟多個線程同時執(zhí)行任務。并發(fā)功能只在異步函數(shù)下才生效幻捏。

并發(fā)隊列 手動創(chuàng)建的串行隊列 主隊列
同步 沒有開辟新線程 串行執(zhí)行 沒有開辟新線程 串行執(zhí)行 沒有開辟新線 串行執(zhí)行
異步 有開辟新線程 并發(fā)執(zhí)行 有開辟新線程 串行執(zhí)行 沒有開辟新線程 串行執(zhí)行

注意:使用同步函數(shù)往當前串行隊列中添加任務盆犁,會卡主當前的串行隊列,產(chǎn)生死鎖篡九。

1.1 同步/異步串行/并發(fā)

存在四種組合方案:

// 同步 + 串行
dispatch_sync(serial_queue, ^{
//任務
});
// 異步 + 串行
dispatch_async(serial_queue, ^{
//任務
});
// 同步 + 并發(fā)
dispatch_sync(concurrent_queue, ^{
//任務
});
// 異步 + 并發(fā)
dispatch_async(concurrent_queue, ^{
//任務
});

1.1.1 同步 + 串行

首先看一道面試題

- (void)viewDidLoad {
  dispatch_sync(dispatch_get_main_queue(), ^{
       [self doSomething];
  });
}

上面這道面試題谐岁,存在什么問題?

產(chǎn)生死鎖。隊列引起循環(huán)等待伊佃。因為窜司,viewDidLoad()進入主隊列,執(zhí)行過程中會將block添加到主隊列中航揉。viewDidLoad()需要等待block執(zhí)行完成后才能結(jié)束塞祈,由于主隊列先進先出的block需要viewDidLoad()執(zhí)行完畢才能執(zhí)行。因此導致隊列循環(huán)等待的問題帅涂。

上面的問題理解议薪,在來看一個問題。

- (void)viewDidLoad {
  dispatch_sync(serial_queue, ^{
       [self doSomething];
  });
}

上面這道題媳友,有什么問題斯议?

沒有問題。這里是將block添加到單獨的串行隊列庆锦。viewDidLoad()在主隊列中在主線程中執(zhí)行捅位,在其執(zhí)行過程中調(diào)用block添加到串行隊列中,在主線程中執(zhí)行搂抒。同步方式提交任務艇搀,無論在串行隊列還是并發(fā)隊列都會在當前線程中執(zhí)行

1.1.2 同步 + 并發(fā)

問題

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        NSLog(@"1");
        dispatch_sync(global_queue, ^{
            NSLog(@"2");
            dispatch_sync(global_queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }
    return 0;
}

上面這段代碼的輸出結(jié)果?

輸出結(jié)果:12345求晶。同步方式提交任務焰雕,無論在串行隊列還是并發(fā)隊列都會在當前線程中執(zhí)行。

思考芳杏,如果換成串行隊列呢矩屁?

1.1.3 異步 + 串行

略...

1.1.4 異步 + 并發(fā)

面試題

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(global_queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
        NSLog(@"3");
    });
}
- (void)printLog {
    NSLog(@"2");
}

上述代碼執(zhí)行的結(jié)果?

結(jié)果:13爵赵。提交異步任務到并發(fā)隊列,任務調(diào)用了performSelector:withObject:afterDelay:吝秕。由于GCD提交的任務是在某個子線程中執(zhí)行,子線程沒有RunLoop空幻。由于performSelector:withObject:afterDelay:需要RunLoop才可生效烁峭,所以方法不執(zhí)行。這個問題秕铛,考察performSelector:withObject:afterDelay:內(nèi)部實現(xiàn)约郁。

任務和隊列示例代碼:任務和隊列Demo包含面試題講解

二、多讀單寫解決方案

  • pthread_rwlock: 讀寫鎖
  • dispatch_barrier_async() 異步柵欄調(diào)用

怎么利用GCD實現(xiàn)多讀單寫但两?或者如何實現(xiàn)多讀單寫鬓梅?

2.1 什么是多讀單寫?

讀者和讀者谨湘,并發(fā)绽快。
讀者和寫者芥丧,互斥。
寫者與寫者坊罢,互斥娄柳。

2.2 解決方法

   dispatch_async(global_queue, ^{
        NSLog(@"讀取1");
    });
   dispatch_async(global_queue, ^{
        NSLog(@"讀取2");
    });
   dispatch_barrier_async(global_queue, ^{
        NSLog(@"寫入1");
    });
   dispatch_async(global_queue, ^{
        NSLog(@"讀取3");
    });
   dispatch_async(global_queue, ^{
        NSLog(@"讀取4");
    });

dispatch_barrier_async函數(shù)會等待追加到并發(fā)隊列上的并行執(zhí)行的處理全部結(jié)束之后,在將指定的處理追加到該并發(fā)隊列中艘绍。然后等dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢后,并發(fā)隊列才恢復為一般的動作秫筏,追加到并發(fā)隊列的處理又開始并行執(zhí)行诱鞠。

三、dispatch_group_async()

面試題:

如何用GCD 實現(xiàn):A这敬、B航夺、C三個任務并發(fā),完成后執(zhí)行任務D崔涂?

實現(xiàn)追加到并發(fā)隊列中的多個任務全部結(jié)束后再執(zhí)行想執(zhí)行的任務阳掐。無論向什么樣的隊列中追加處理,使用Dispatch Group都可監(jiān)視這些處理執(zhí)行的結(jié)束冷蚂。一旦檢測到所有處理執(zhí)行結(jié)束缭保,該Dispatch Group與隊列相同。

dispatch_group_async()
同dispatch_async()函數(shù)相同蝙茶,都追加Block到指定的Dispatch Queue中艺骂。當組中所有任務都執(zhí)行完成后,dispatch_group_notify()執(zhí)行Block中的內(nèi)容隆夯。

示例代碼:

// dispatch_group_notify
- (void)test {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.lqq.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任務1 - %@", [NSThread currentThread]);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任務2 - %@", [NSThread currentThread]);
        }
    });
    //--------------示例1-------------------
    // 寫法一钳恕, 等上面的任務執(zhí)行完成后,才會在主隊列中執(zhí)行任務3
//    dispatch_group_notify(group, queue, ^{
//        dispatch_async(dispatch_get_main_queue(), ^{
//            for (int i = 0; i < 5; i++) {
//                NSLog(@"任務3 - %@", [NSThread currentThread]);
//            }
//        });
//    });

    //寫法二:直接在主隊列中執(zhí)行
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        for (int i = 0; i < 5; i++) {
//            NSLog(@"任務3 - %@", [NSThread currentThread]);
//        }
//    });
    //--------------示例2-------------------
    // 如果有多個notify會怎么執(zhí)行呢蹄衷?
    dispatch_group_notify(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任務3 - %@", [NSThread currentThread]);
        }
    });
    dispatch_group_notify(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任務4 - %@", [NSThread currentThread]);
        }
    });
    // 任務3和任務4是交替執(zhí)行的
}

另外忧额,也可以使用dispatch_group_wait,如下:

   // 監(jiān)控任務是否完成愧口,當完成時會返回0睦番,不完成一直等待。
    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    if (result == 0) {
        NSLog(@"全部任務執(zhí)行完成");
    }

線程組示例代碼:線程組API使用Demo

四调卑、NSOperation

需要和NSOperationQueue配合使用來實現(xiàn)多線程抡砂。優(yōu)勢和特點:添加任務依賴、任務執(zhí)行狀態(tài)控制恬涧、控制最大并發(fā)量注益。

任務狀態(tài)的控制

isReady
isExecuting
isFinished
isCanceled

如果重寫main方法,底層控制變更任務執(zhí)行完成狀態(tài)溯捆,以及任務退出丑搔。

如果重寫start方法厦瓢,自行控制任務狀態(tài)。

面試題

系統(tǒng)是怎樣移除一個isFinished=YES的NSOperation的啤月?答案:KVO

五煮仇、NSThread

啟動流程

start() -> 創(chuàng)建pthread -> main() ->[target performSelector:selector] -> exit()

如何實現(xiàn)常駐進程?
通過使用NSThread和RunLoop實現(xiàn)谎仲。

附錄

GCD API介紹:
dispatch_queue_create(名稱浙垫,類型)
類型為NULL時,為串行隊列郑诺;為DISPATCH_QUEUE_CONCUREENT夹姥,為并行隊列.。

需要手動release釋放隊列
dispatch_release(隊列)

主隊列辙诞,串行
dispatch_get_main_queue()
dispatch_get_global_queue(優(yōu)先級辙售,0)

優(yōu)先級有四種情況:
    高,默認飞涂,低旦部,后臺

為自己創(chuàng)建的隊列設(shè)置優(yōu)先級

dispatch_set_target_queue(自定義隊列,其他已知優(yōu)先級的隊列) 

dispatch_after(時間较店,隊列士八,Block)
時間:dispatch_time_t 

dispatch_group 監(jiān)聽所有任務結(jié)束。

dispatch_group_create() //創(chuàng)建一個隊列組梁呈,需要手動release
dispatch_group_async(組曹铃,隊列,block)
dispatch_group_notify(組捧杉,隊列陕见,block) // 所有任務都結(jié)束后調(diào)用
也可以使用dispatch_group_wait()

dispatch_barrier_async() 一般用于數(shù)據(jù)庫操作和文件讀寫。

同步任務
dispatch_sync(隊列味抖,block) 

異步任務
dispatch_async(隊列评甜,block)

指定執(zhí)行次數(shù)
dispatch_apply(次數(shù),隊列仔涩,block)

掛起
dispatch_suspend(隊列)

恢復
dispatch_resume(隊列)

信號量
Dispatch Semaphore是持有計數(shù)的信號忍坷,該計數(shù)是多線程編程中的計數(shù)類型信號。

dispatch_semaphore_create(技術(shù)值)
dispatch_semaphore_wait(semaphore, 時間)
dispatch_semaphore_signal(semaphore)
dispatch_reliease(semaphore)

// 示例
// 創(chuàng)建一個對象熔脂,默認的計數(shù)值為0佩研,等待。當計數(shù)值大于1或者等于1時霞揉,減去1而不等待旬薯。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 發(fā)送計數(shù)值加一信號
dispatch_semaphore_signal(semaphore);
// 等待計數(shù)值變化,第二個參數(shù)是等待時間适秩,這里是永久等待绊序。
// 當計數(shù)值大于0時硕舆,返回0。等于0不會返回任何值骤公。
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

if (result == 0) {
    NSLog(@"執(zhí)行排他性操作");
}

dispatch_once函數(shù)是保證在應用程序執(zhí)行中只執(zhí)行一次的API抚官。

dispatch I/O 分割讀取,提高讀取效率阶捆。

小結(jié)

通過幾道面試題理解iOS中幾種多線程解決方案凌节。筆者認為該課程可以用作知識梳理,將解讀的內(nèi)容不是很全面洒试,其他的內(nèi)容可以看《小馬哥底層原理視頻》刊咳。

參考文章:

細說 NSOperation

原文鏈接

iOS開發(fā)進階-多線程技術(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市儡司,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌余指,老刑警劉巖捕犬,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異酵镜,居然都是意外死亡碉碉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門淮韭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垢粮,“玉大人,你說我怎么就攤上這事靠粪±桑” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵占键,是天一觀的道長昔善。 經(jīng)常有香客問我,道長畔乙,這世上最難降的妖魔是什么君仆? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮牲距,結(jié)果婚禮上返咱,老公的妹妹穿的比我還像新娘。我一直安慰自己牍鞠,他們只是感情好咖摹,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著难述,像睡著了一般楞艾。 火紅的嫁衣襯著肌膚如雪参咙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天硫眯,我揣著相機與錄音蕴侧,去河邊找鬼。 笑死两入,一個胖子當著我的面吹牛净宵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裹纳,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼择葡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了剃氧?” 一聲冷哼從身側(cè)響起敏储,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朋鞍,沒想到半個月后已添,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡滥酥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年更舞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坎吻。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡缆蝉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘦真,到底是詐尸還是另有隱情刊头,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布诸尽,位于F島的核電站芽偏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏弦讽。R本人自食惡果不足惜污尉,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望往产。 院中可真熱鬧被碗,春花似錦、人聲如沸仿村。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔼囊。三九已至焚志,卻和暖如春衣迷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酱酬。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工壶谒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膳沽。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓汗菜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挑社。 傳聞我的和親對象是個殘疾皇子陨界,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容

  • iOS中多線程 首先看一道面試題 iOS中多線程有哪些實現(xiàn)方案? iOS中痛阻,多線程一般有三種方案GCD菌瘪、NSOpe...
    繁華落盡丶lee閱讀 809評論 0 3
  • iOS多線程編程 基本知識 1. 進程(process) 進程是指在系統(tǒng)中正在運行的一個應用程序,就是一段程序的執(zhí)...
    陵無山閱讀 6,028評論 1 14
  • 本文用來介紹 iOS 多線程中 GCD 的相關(guān)知識以及使用方法阱当。這大概是史上最詳細俏扩、清晰的關(guān)于 GCD 的詳細講...
    花花世界的孤獨行者閱讀 497評論 0 1
  • 如何把柴米變成詩 如何讓我的腳丫不矜持 知道的它會降臨 不知道的也能悄然而至 聽說溫柔能攤平歲月的眼角 瞧,撐了傘...
    趙清辭閱讀 192評論 0 0
  • 昨日放假啤斗,叫了三四個好友過來家里聚餐表箭。大家一起買菜,洗菜钮莲,做飯免钻。回歸生活的感覺真好崔拥,平平淡淡极舔,簡簡單單,大家聊天链瓦,...
    遇見Ellen閱讀 356評論 0 1