11--多線程01--多線程概念(線程和進程)

[TOC]

一或粮、 線程和進程

1.1 線程的定義

  • 線程是進程的基本執(zhí)行單元塔淤,一個進程的所有任務倒在線程中執(zhí)行
  • 進程要想執(zhí)行任務,必須得有線程针史,進程至少要有一條線程
  • 程序啟動會默認開啟一條線程贡定,這條線程被稱為主線程或 UI 線程

1.2 進程的定義

  • 進程是指在系統(tǒng)中正在運行的一個應用程序
  • 每個進程之間是獨立的赋访,每個進程均運行在其專用的且受保護的內存

1.3 iOS開發(fā)為什么是單進程的

  • 沙盒-單進程數據更加隱私、安全
  • 進程切換缓待,消耗資源大

1.4 線程和隊列的關系

  • 線程和隊列是兩個完全獨立的概念蚓耽,線程是由系統(tǒng)分配、系統(tǒng)調度旋炒,隊列是用來管理任務步悠,開發(fā)者可以管理任務的串行、并行瘫镇、優(yōu)先級鼎兽、先后順序;
  • 開發(fā)者管理任務隊列铣除,系統(tǒng)根據隊列的屬性谚咬,來給隊列中的任務分配線程;
  • 隊列是提供給開發(fā)者使用的抽象概念通孽,線程是由系統(tǒng)調度的真實對象序宦,開發(fā)者可以通過管理隊列來指定系統(tǒng)分配、調度線程的數量背苦、順序等互捌。
- 任務執(zhí)行速度:CPU、線程行剂、隊列秕噪、任務的復雜度、任務的優(yōu)先級
- 大量的臨時變量:使用 autorealease 處理

1.5 線程和進程的關系和區(qū)別

  • 地址空間:同一進程的線程共享本進程的地址空間厚宰,而進程之間則是獨立的地址空間
  • 資源擁有:同一進程內的線程共享進程的資源腌巾,如內存遂填、I/O、cpu等澈蝙,但是進程之間的資源是獨立的
  • 健壯性:一個進程崩潰后吓坚,在保護模式下不會對其他進程產生影響,但是一個線程崩潰整個進程都會死掉灯荧。多以多進程要比多線程健壯
  • 并發(fā)操作:進程切換時礁击,消耗的資源大,效率高逗载。所以涉及到頻繁的切換時哆窿,使用線程要好于進程。同樣如果要求同時進行并且又要共享某些變量的并發(fā)操作厉斟,只能使用線程不能用進程
  • 執(zhí)行過程:每個獨立的進程有一個程序運行的入口挚躯、順序執(zhí)行序列和程序入口。但是線程不能獨立執(zhí)行擦秽,必須依存于應用程序中码荔,由應用程序提供多個線程執(zhí)行控制
  • 調度單位:線程是處理器調度的基本單位,但進程不是

1.6 進程和線程與堆和棧的關系

  • 進程的地址空間
    每個進程的地址空間是獨立的号涯,是操作系統(tǒng)把物理內存映射到進程的虛擬地址上目胡。
    進程只能訪問自己的地址空間锯七,不能訪問別的進程的地址空間链快。
    通俗的理解:如果有a.exe和b.exe同時在系統(tǒng)中運行,那么a和b都可以有0x00000000-0xffffffff的虛擬地址空間(不考慮操作系統(tǒng)占用等因素)眉尸。
    假如a b都有1個變量在地址 0x12345678處域蜗,看起來地址一樣,實際上在物理內存中不是一個地方噪猾。
  • 進程是線程的容器霉祸,windows調度運行的單位是線程,一個進程至少有1個主線程袱蜡。一個進程內所有的線程都處于同一個虛擬地址空間丝蹭。
  • 進程初始化的時候,系統(tǒng)會在進程的地址空間中創(chuàng)建一個堆坪蚁,叫進程默認堆奔穿。進程中所有的線程共用這一個堆。當然敏晤,可以增加1個或幾個堆贱田,給不同的線程共同使用或單獨使用。
  • 創(chuàng)建線程的時候嘴脾,系統(tǒng)會在進程的地址空間中分配1塊內存給線程棧男摧,通常是1MB。線程棧是獨立的,不共享耗拓。iOS中是512KB

1.7 補充內存相關知識

  • 內存五大分區(qū)
    1拇颅、棧區(qū)(stack):由編譯器自動分配釋放,存放函數的參數值乔询,局部變量的值等蔬蕊。其操作方式類似于數據結構中的棧。
    2哥谷、堆區(qū)(heap):一般由程序員分配釋放岸夯,若程序員不釋放,程序結束時可能由OS回收们妥。注意它與數據結構中的堆是兩回事猜扮,分配方式類似于鏈表。new出來的放在這里监婶。
    3旅赢、全局區(qū)(靜態(tài)區(qū)):(static)全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域惑惶,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域煮盼。程序結束后由系統(tǒng)釋放。
    4带污、文字常量區(qū):常量字符串就是放在這里的僵控。程序結束后由系統(tǒng)釋放
    5、程序代碼區(qū):存放函數體的二進制代碼鱼冀。

  • 棧和堆的區(qū)別

    • 管理方式不同报破。棧由操作系統(tǒng)自動分配釋放,無需我們手動控制千绪;堆的申請和釋放工作由程序員控制充易,容易產生內存泄漏;
    • 空間大小不同荸型。每個進程擁有的棧的大小要遠遠小于堆的大小盹靴。理論上,程序員可申請的堆大小為虛擬內存的大小瑞妇,進程棧的大小64bits的Windows默認1MB稿静,64bits的Linux默認10MB;
    • 生長方向不同踪宠。堆的生長方向向上自赔,內存地址由低到高;棧的生長方向向下柳琢,內存地址由高到低绍妨。
    • 分配方式不同润脸。
      堆都是動態(tài)分配的,沒有靜態(tài)分配的堆他去。
      棧有2種分配方式:靜態(tài)分配和動態(tài)分配毙驯。靜態(tài)分配是由操作系統(tǒng)完成的,比如局部變量的分配灾测。
      棧的動態(tài)分配由alloca函數進行分配爆价,但是棧的動態(tài)分配和堆是不同的,棧的動態(tài)分配是由操作系統(tǒng)進行釋放媳搪,無需我們手工實現铭段。
    • 分配效率不同
      棧由操作系統(tǒng)自動分配秦爆,會在硬件層級對棧提供支持:分配專門的寄存器存放棧的地址序愚,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高等限。
      堆則是由C/C++提供的庫函數或運算符來完成申請與管理爸吮,實現機制較為復雜,頻繁的內存申請容易產生內存碎片望门。顯然形娇,堆的效率比棧要低得多。

二筹误、 多線程

2.1 多線程的意義

  • 優(yōu)點
    • 能適當提高程序的執(zhí)行效率
    • 能適當提高資源的利用率(CPU桐早、內存)
    • 線程上的任務執(zhí)行完成后,線程會自動銷毀
  • 缺點
    • 開啟線程需要占用一定的內存空阿金(默認情況下纫事,每一個線程都占 512 KB)
    • 如果開啟大量的線程勘畔,會占用大量的內存空間所灸,降低程序的性能
    • 線程越多丽惶,CPU 在調用線程上的開銷就越大
    • 程序設計更加復雜,比如線程間的通信爬立、多線程的數據共享

2.2 多線程的原理

CPU 在單位時間片里快速在各個線程之間切換

  • 單線程
    單核 CPU

    單線程
        任務1
        任務2
        任務3
    
    • 其本質也是單線程钾唬,一次只能執(zhí)行一個任務
    • 單核 CPU的多線程是通過 任務1執(zhí)行一部分再執(zhí)行任務2再切換任務3,這種方式達到多線程的效果
    • 不停切換侠驯、時間片抡秆、共贏、達到利益最大化
  • 多線程
    多核 CPU

    線程1
        任務1
    線程2
        任務2
    線程3
        任務3
    

2.3 多線程技術方案

  • pthread

    • 一套通用的多線程API
    • 適用于 Unix/Linux/Windows 等系統(tǒng)
    • 跨平臺/可移植
    • 使用難度大
  • NSThread

    • 使用更加面向對象
    • 簡單易用吟策,可直接操作線程對象
  • GCD

    • 旨在替代 NSTread 等線程技術
    • 充分利用設備的多核
  • NSOperation

    • 基于GCD(底層是GCD)
    • 比 GCD 多了一些更簡單使用的功能
    • 使用更加面向對象

三儒士、線程、線程池檩坚、生命周期着撩、安全诅福、通訊、runloop

3.1 線程的生命周期

  • 新建:Start拖叙,創(chuàng)建線程氓润,調用start進入就緒狀態(tài)

  • 就緒:Runnable,CPU調度當前線程薯鳍,進入運行狀態(tài)

  • 運行:Running咖气,任務執(zhí)行完畢,退出線程并銷毀挖滤,CPU調度當前線程

  • 阻塞:Blocked崩溪,調用 sleep方法/等待同步鎖/從可調度線程池移出

  • 死亡:Dead,任務執(zhí)行完成斩松、強制退出

  • 可調度線程池

    • 當前線程
    • 其他線程
    • CPU調度的線程是從線程池中獲取
  • 正常流程和堵塞流程

    • 正常流程:新建->就緒->運行->死亡
    • 堵塞流程:新建->就緒->運行->堵塞->就緒->...->堵塞
      image.png

3.2 線程池的原理

  • 參數解釋
    • corePoolSize:線程池的基本大忻踔邸(核心線程池大小)
    • maxinumPoolSize:線程池的最大大小
    • keepAliveTime:線程池中超過corePoolSize數目的空閑線程的最大存活時間
    • unit:keepAliveTime參數的時間單位
    • workQueue:任務阻塞隊列
    • threadFactory:新建線程的工廠
    • handler:當提交的任務數超過maxnumPoolSize與workQueue之和時砸民,任務會交給RejectedExecutionHandler來處理
  • 【原理】線程池大小小于核心線程池大小
    • 創(chuàng)建線程執(zhí)行任務抵怎,流程和線程的生命周期一樣
  • 【原理】線程池大小不小于核心線程池大小
    • 線程池判斷工作隊列未滿
      將任務push進隊列
    • 線程池判斷工作隊列已滿
      • maxinumPoolSize>corePoolSize,將創(chuàng)建新的線程來執(zhí)行任務
      • 交給報策略去處理
        1. Abort策略:默認策略岭参,新任務提交時直接拋出未檢查的異常 RejectedExecution反惕,該異常可由調用者捕獲
        2. CallerRuns策略:為調節(jié)機制演侯,既不拋棄任務也不拋出異常姿染,而是將某些任務會退到調用者。不會在線程池的線程中執(zhí)行新的任務秒际,而是在調用exector的線程中運行新的任務
        3. Discard策略:新提交的任務被拋棄悬赏。
        4. DiscardOldest策略:隊列的是“對頭”的任務,然后嘗試提交新的任務娄徊。(不適合工作隊列為優(yōu)先隊列場景)

3.3 線程和runloop的關系

  • runloop與線程的對應關系
    • runloop與線程是一一對應的闽颇,一個runloop對應一個核心的線程;
    • 為什么說是核心的寄锐?因為runloop是可以嵌套的兵多,但是核心的只能有一個,他們的關系保存在一個全局的字典里橄仆;
  • runloop是來管理線程的剩膘,當線程的runloop被開啟后,線程會在執(zhí)行完成后進入休眠狀態(tài)盆顾,有了任務就會被喚醒去執(zhí)行任務怠褐。
  • runloop在第一次或失去時被創(chuàng)建,在線程結束時被銷毀您宪。
  • 對于主線程來說奈懒,runloop在程序一啟動就默認創(chuàng)建好了具温。
  • 對于子線程來說,runloop是懶加載的筐赔,只有當我們使用的時候才會創(chuàng)建铣猩,所以在子線程用定時器要注意:確保子線程的runloop被創(chuàng)建,不然定時器不會回調茴丰。

3.4 線程安全和線程通訊

  • atomicnonatomic的區(qū)別

    • 屬性定義
      • nonatomic:非原子屬性达皿,適合內存小的移動設備
      • atomic:原子屬性(線程安全),針對多線程設計贿肩,默認值峦椰,需要消耗大量的資源。atomic只保證了寫安全汰规,但對于可變數組這類容器中的元素的讀寫安全并不能保證汤功。
    • 線程安全解釋
      • 保證同一時間只有一個線程能夠寫入
      • atomic本身就有一把鎖(自旋鎖)
      • 單寫多讀:單個線程寫入,多個線程可以讀取
    • iOS開發(fā)的建議
      • 所有屬性都聲明為 nonatomic
      • 盡量避免多線程搶奪一塊資源
      • 盡量將加鎖溜哮、資源搶奪的業(yè)務邏輯交給服務器端處理滔金,減小移動客戶端的壓力
  • 互斥鎖小結

    • 保證鎖內的代碼,同一時間茂嗓,只有一條線程能夠執(zhí)行
    • 互斥鎖的鎖定范圍餐茵,應該盡量小,鎖定范圍越大述吸,效率越差
    • 互斥鎖參數
      • 能夠加鎖的任意 NSObject 對象
      • 注意:鎖對象一定要保證所有的線程都能夠訪問
      • 如果代碼中只有一個地方需要加鎖忿族,大多都使用 self,這樣可以避免單獨再創(chuàng)建一個鎖對象

四蝌矛、iOS中常用多線程技術

4.1 pthread

pthread_create 創(chuàng)建線程

  • 參數:

    1. pthread_t:要創(chuàng)建線程的結構體指針道批,通常開發(fā)的時候,如果遇到 C 語言的結構體入撒,類型后綴 _t / Ref 結尾
      同時不需要 *
    2. 線程的屬性隆豹,nil(空對象 - OC 使用的) / NULL(空地址,C 使用的)
    3. 線程要執(zhí)行的函數地址
      void *: 返回類型衅金,表示指向任意對象的指針噪伊,和 OC 中的 id 類似
      (*): 函數名
      (void *): 參數類型,void *
    4. 傳遞給第三個參數(函數)的參數
      • 返回值:C 語言框架中非常常見
        int
        0 創(chuàng)建線程成功氮唯!成功只有一種可能
        非 0 創(chuàng)建線程失敗的錯誤碼,失敗有多種可能姨伟!
        // 1: pthread
        pthread_t threadId = NULL;
        //c字符串
        char *cString = "HelloCode";
    
        int result = pthread_create(&threadId, NULL, pthreadTest, cString);
        if (result == 0) {
            NSLog(@"成功");
        } else {
            NSLog(@"失敗");
        }
    
        void *pthreadTest(void *para){
            // 接 C 語言的字符串
            //    NSLog(@"===> %@ %s", [NSThread currentThread], para);
            // __bridge 將 C 語言的類型橋接到 OC 的類型
            NSString *name = (__bridge NSString *)(para);
            
            NSLog(@"===>%@ %@", [NSThread currentThread], name);
            
            return NULL;
        }
    
4.2 NSThread
[NSThread detachNewThreadSelector:@selector(threadTest) toTarget:self withObject:nil];
/**
 1. 循環(huán)的執(zhí)行速度很快
 2. 棧區(qū)/常量區(qū)的內存操作也挺快
 3. 堆區(qū)的內存操作有點慢
 4. I(Input輸入) / O(Output 輸出) 操作的速度是最慢的惩琉!
 * 會嚴重的造成界面的卡頓,影響用戶體驗夺荒!
 * 多線程:開啟一條線程瞒渠,將耗時的操作放在新的線程中執(zhí)行
 */
- (void)threadTest{
    NSLog(@"begin");
    NSInteger count = 1000 * 100;
    for (NSInteger i = 0; i < count; i++) {
        // 棧區(qū)
        NSInteger num = I;
        // 常量區(qū)
        NSString *name = @"zhang";
        // 堆區(qū)
        NSString *myName = [NSString stringWithFormat:@"%@ - %zd", name, num];
        NSLog(@"%@", myName);
    }
    NSLog(@"over");
}
4.3 GCD
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self threadTest];
});
4.4 NSOperation
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
    [self threadTest];
}];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末良蒸,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子伍玖,更是在濱河造成了極大的恐慌嫩痰,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窍箍,死亡現場離奇詭異串纺,居然都是意外死亡,警方通過查閱死者的電腦和手機椰棘,發(fā)現死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門纺棺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人邪狞,你說我怎么就攤上這事祷蝌。” “怎么了帆卓?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵巨朦,是天一觀的道長。 經常有香客問我剑令,道長罪郊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任尚洽,我火速辦了婚禮悔橄,結果婚禮上,老公的妹妹穿的比我還像新娘腺毫。我一直安慰自己癣疟,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布潮酒。 她就那樣靜靜地躺著睛挚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪急黎。 梳的紋絲不亂的頭發(fā)上扎狱,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音勃教,去河邊找鬼淤击。 笑死,一個胖子當著我的面吹牛故源,可吹牛的內容都是我干的污抬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼印机!你這毒婦竟也來了矢腻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤射赛,失蹤者是張志新(化名)和其女友劉穎多柑,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體楣责,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡竣灌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了腐魂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帐偎。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蛔屹,靈堂內的尸體忽然破棺而出削樊,到底是詐尸還是另有隱情,我是刑警寧澤兔毒,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布漫贞,位于F島的核電站,受9級特大地震影響育叁,放射性物質發(fā)生泄漏迅脐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一豪嗽、第九天 我趴在偏房一處隱蔽的房頂上張望谴蔑。 院中可真熱鬧,春花似錦龟梦、人聲如沸隐锭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钦睡。三九已至,卻和暖如春躁倒,著一層夾襖步出監(jiān)牢的瞬間荞怒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工秧秉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褐桌,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓福贞,卻偏偏與公主長得像撩嚼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挖帘,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容