iOS筆記-多線程相關(guān)(pthread 肪笋、NSThread 月劈、GCD度迂、NSOperation)

隨機配圖
  • 進程

    • 進程是指在系統(tǒng)中正在運行的一個應(yīng)用程序
  • 線程

    • 1個進程要想執(zhí)行任務(wù),必須得有線程(每1個進程至少要有1條線程)
    • 1個線程中任務(wù)的執(zhí)行是串行的(執(zhí)行完上一個才能執(zhí)行下一個)
  • 多線程

    • 1個進程中可以開啟多條線程猜揪,多條線程可以并行(同時)執(zhí)行不同的任務(wù)
    • 線程可以并行, 但是每個線程中的任務(wù)還是串行
  • 多線程原理

    • 多線程并發(fā)(同時)執(zhí)行惭墓,其實是CPU快速地在多條線程之間調(diào)度(切換)
  • 多線程優(yōu)缺點

    • 優(yōu)點
      • 能適當提高程序的執(zhí)行效率
      • 能適當提高資源利用率(CPU、內(nèi)存利用率)
    • 缺點
      • 線程越多而姐,CPU在調(diào)度線程上的開銷就越大
      • 如果開啟大量的線程腊凶,會降低程序的性能
      • 程序設(shè)計更加復雜:比如線程之間的通信、多線程的數(shù)據(jù)共享

pthread

  • 類型:
    C語言中類型的結(jié)尾通常 _t/Ref毅人,而且不需要使用 *
/*
參數(shù):
     1. 線程代號的地址
     2. 線程的屬性
     3. 調(diào)用函數(shù)的指針
        - void *(*)(void *)
        - 返回值 (函數(shù)指針)(參數(shù))
        - void * 和 OC 中的 id 是等價的
     4. 傳遞給該函數(shù)的參數(shù)
返回值:
     如果是0吭狡,表示正確
     如果是非0,表示錯誤碼
*/
NSString *str = @"lnj";
    pthread_t thid;
    int res = pthread_create(&thid, NULL, &demo, (__bridge void *)(str));
    if (res == 0) {
        NSLog(@"OK");
    } else {
        NSLog(@"error %d", res);
    }

NSThread

  • 一個NSThread對象就代表一條線程
  • 創(chuàng)建線程的幾種方式
  • alloc/init
    // 1.創(chuàng)建線程
    NJThread *thread = [[NJThread alloc] initWithTarget:self selector:@selector(demo:) object:@"lnj"];
    // 設(shè)置線程名稱
    [thread setName:@"xmg"];
    // 設(shè)置線程的優(yōu)先級
    // 優(yōu)先級僅僅說明被CPU調(diào)用的可能性更大
    [thread setThreadPriority:1.0];
    // 2.啟動線程
    [thread start];

  • detach/performSelector
    • 優(yōu)點:簡單快捷
    • 缺點:無法對線程進行更詳細的設(shè)置
// 1.創(chuàng)建線程
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"lnj"];

// 1.創(chuàng)建線程
// 注意: Swift中不能使用, 蘋果認為這個方法不安全
    [self performSelectorInBackground:@selector(demo:) withObject:@"lnj"];

  • 多線程的安全隱患
    • 被鎖定的代碼同一時刻只能有一個線程執(zhí)行
@synchronized(鎖對象) { // 需要鎖定的代碼  }

  • 互斥鎖的優(yōu)缺點
    優(yōu)點:能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
    缺點:需要消耗大量的CPU資源

  • 互斥鎖注意點

    • 鎖定1份代碼只用1把鎖丈莺,用多把鎖是無效的
    • 鎖定范圍越大, 性能越差

  • 原子和非原子屬性

    • atomic:線程安全划煮,需要消耗大量的資源
    • nonatomic:非線程安全,適合內(nèi)存小的移動設(shè)備
  • 自旋鎖 & 互斥鎖

    • 共同點
      都能夠保證同一時間缔俄,只有一條線程執(zhí)行鎖定范圍的代碼
    • 不同點
      • 互斥鎖:如果發(fā)現(xiàn)有其他線程正在執(zhí)行鎖定的代碼弛秋,線程會進入"休眠"狀態(tài),等待其他線程執(zhí)行完畢俐载,打開鎖之后蟹略,線程會被"喚醒"
      • 自旋鎖:如果發(fā)現(xiàn)有其他線程正在執(zhí)行鎖定的代碼,線程會"一直等待"鎖定代碼執(zhí)行完成遏佣!
        自旋鎖更適合執(zhí)行非常短的代碼挖炬!

  • 線程間通信

    • 子線程做耗時操作, 主線程更新數(shù)據(jù)

[self performSelectorInBackground:@selector(download) withObject:nil];

/*
 waitUntilDone是否等待被調(diào)用方法執(zhí)行完成,有可能也會等待調(diào)用方法的執(zhí)行完成状婶!
 YES: 等待被調(diào)用線程執(zhí)行完畢再執(zhí)行后面的代碼
 NO : 不用等待被調(diào)用線程執(zhí)行完畢就可以執(zhí)行后面的代碼
 */

[self performSelectorOnMainThread:@selector(showImage:) withObject:[UIImage imageWithData:data] waitUntilDone:YES];


---

###GCD
- GCD中有2個核心概念
  + 任務(wù):執(zhí)行什么操作
  + 隊列:用來存放任務(wù)

- 執(zhí)行任務(wù)
  + 同步方法: dispatch_sync
  + 異步方法: dispatch_async
  + 同步和異步的區(qū)別
      * 同步:只能在當前線程中執(zhí)行任務(wù)意敛,不具備開啟新線程的能力
      * 異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力

- 隊列
  + 并發(fā)隊列
      * 可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
      * 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
  + 串行隊列
      * 讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后膛虫,再執(zhí)行下一個任務(wù))

- 注意點
  + 同步和異步主要影響:能不能開啟新的線程
      * 同步:只是在當前線程中執(zhí)行任務(wù)草姻,不具備開啟新線程的能力
      * 異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
  + 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
      * 并發(fā):允許多個任務(wù)并發(fā)(同時)執(zhí)行
      * 串行:一個任務(wù)執(zhí)行完畢后稍刀,再執(zhí)行下一個任務(wù)

- 各種任務(wù)隊列搭配
  + 同步 + 串行
  + 同步 + 并發(fā)
  + 異步 + 串行
  + 異步 + 并發(fā)
  + 異步 + 主隊列
  + 同步 + 主隊列
      

- GCD線程間通信

```objc
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  // 執(zhí)行耗時的異步操作...
    dispatch_async(dispatch_get_main_queue(), ^{
      // 回到主線程撩独,執(zhí)行UI刷新操作
      });
});
  • GCD其它用法
  • 延時執(zhí)行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2秒后執(zhí)行這里的代碼...
});
  • 一次性代碼

    • 使用dispatch_once函數(shù)能保證某段代碼在程序運行過程中只被執(zhí)行1次

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認是線程安全的)
});


- 快速迭代

```objc
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
  // 執(zhí)行10次代碼,index順序不確定
});
  • barrier

    • 在前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行账月,而且它后面的任務(wù)等它執(zhí)行完成之后才會執(zhí)行
    • 不能是全局的并發(fā)隊列
    • 所有的任務(wù)都必須在一個隊列中

dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);


- 隊列組

```objc
dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  // 執(zhí)行1個耗時的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  // 執(zhí)行1個耗時的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
  // 等前面的異步操作都執(zhí)行完畢后综膀,回到主線程...
});

NSOperation

  • NSOperation的作用

    • 配合使用NSOperation和NSOperationQueue也能實現(xiàn)多線程編程
  • NSOperation和NSOperationQueue實現(xiàn)多線程的具體步驟

    • 先將需要執(zhí)行的操作封裝到一個NSOperation對象中
    • 然后將NSOperation對象添加到NSOperationQueue中
    • 系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來
    • 將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行
  • NSOperation的子類

  • NSOperation是個抽象類,并不具備封裝操作的能力捶障,必須使用它的子類

使用NSOperation子類的方式有3種

1.NSInvocationOperation
2.NSBlockOperation
3.自定義子類繼承NSOperation僧须,實現(xiàn)內(nèi)部相應(yīng)的方法
  • 1.NSInvocationOperation
    • 1.創(chuàng)建NSInvocationOperation對象
      - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

    • 2.調(diào)用start方法開始執(zhí)行操作
      - (void)start;

    • 一旦執(zhí)行操作,就會調(diào)用target的sel方法

注意
- 默認情況下项炼,調(diào)用了start方法后并不會開一條新線程去執(zhí)行操作担平,而是在當前線程同步執(zhí)行操作
- 只有將NSOperation放到一個NSOperationQueue中示绊,才會異步執(zhí)行操作

  • 2.NSBlockOperation
    • 1.創(chuàng)建NSBlockOperation對象
      + (id)blockOperationWithBlock:(void (^)(void))block;

    • 通過addExecutionBlock:方法添加更多的操作
      - (void)addExecutionBlock:(void (^)(void))block;

注意
- 只要NSBlockOperation封裝的操作數(shù) > 1,就會異步執(zhí)行操作


NSOperationQueue

  • NSOperationQueue的作用

    • NSOperation可以調(diào)用start方法來執(zhí)行任務(wù)暂论,但默認是同步執(zhí)行的
    • 如果將NSOperation添加到NSOperationQueue(操作隊列)中面褐,系統(tǒng)會自動異步執(zhí)行NSOperation中的操作
  • 添加操作到NSOperationQueue中
    - (void)addOperation:(NSOperation *)op;
    - (void)addOperationWithBlock:(void (^)(void))block;

最大并發(fā)數(shù)

  • 什么是并發(fā)數(shù)

    • 同時執(zhí)行的任務(wù)數(shù)
    • 比如,同時開3個線程執(zhí)行3個任務(wù)取胎,并發(fā)數(shù)就是3
  • 最大并發(fā)數(shù)的相關(guān)方法
    - (NSInteger)maxConcurrentOperationCount;
    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

隊列的取消展哭、暫停、恢復

  • 取消隊列的所有操作

    - (void)cancelAllOperations;

    提示:也可以調(diào)用NSOperation的- (void)cancel方法取消單個操作

  • 暫停和恢復隊列
    - (void)setSuspended:(BOOL)b; // YES代表暫停隊列闻蛀,NO代表恢復隊列
    - (BOOL)isSuspended;

操作依賴

  • NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序
    • 比如一定要讓操作A執(zhí)行完后匪傍,才能執(zhí)行操作B,可以這么寫
      [operationB addDependency:operationA]; // 操作B依賴于操作A

    • 可以在不同queue的NSOperation之間創(chuàng)建依賴關(guān)系

操作的監(jiān)聽

  • 可以監(jiān)聽一個操作的執(zhí)行完畢
    - (void (^)(void))completionBlock;
    - (void)setCompletionBlock:(void (^)(void))block;

自定義NSOperation

  • 自定義NSOperation的步驟
    • 重寫- (void)main方法觉痛,在里面實現(xiàn)想執(zhí)行的任務(wù)

    • 重寫- (void)main方法的注意點:

      • 自己創(chuàng)建自動釋放池(因為如果是異步操作役衡,無法訪問主線程的自動釋放池)
      • 經(jīng)常通過- (BOOL)isCancelled方法檢測操作是否被取消,對取消做出響應(yīng)

NSOperationQueue和GCD對比

+ GCD
    * 并發(fā): 自己創(chuàng)建, 全局
    * 串行: 自己創(chuàng)建, 主隊列
+ NSOperationQueue
    * 主隊列: mainQueue
        + 永遠在主線程中執(zhí)行
    * 自己創(chuàng)建隊列: alloc init
        + 會開啟新的線程, 在子線程中執(zhí)行
+ 如何控制并行和串行
    * maxConcurrentOperationCount = -1 ; 并行
    * 默認就是并行
    * maxConcurrentOperationCount = 1 ; 串行
    * maxConcurrentOperationCount = 0 ; 不會執(zhí)行
+ 使用步驟:
    * 和GCD一樣
    * 1.創(chuàng)建操作(任務(wù))
    * 2.將任務(wù)添加到隊列中
+ 快速添加任務(wù)的方法
// 只要利用隊列調(diào)用addOperationWithBlock:方法, 系統(tǒng)內(nèi)部會自動封裝成一個NSBlockOperation \
    然后再添加到隊列中
[queue addOperationWithBlock:^{
        NSLog(@"3 == %@", [NSThread currentThread]);
    }];
  • 隊列的暫停和恢復以及取消
    • 暫停
      • self.queue.suspended = YES;
      • 注意點:暫停其實是暫停下一個任務(wù), 而不能暫停當前任務(wù)
    • 恢復
      • self.queue.suspended = NO;
      • 注意點: 恢復之后會繼續(xù)執(zhí)行隊列中沒有被執(zhí)行的操作
    • 取消
      • [self.queue cancelAllOperations];
      • 實現(xiàn)原理: 調(diào)用所有操作的cancel方法
      • 注意點: 取消其實是取消下一個任務(wù), 而不能取消當前任務(wù)
      • 如果自定義操作中做了很多耗時操作, 蘋果建議定期檢查是否已經(jīng)取消了

- (void)main
{
    // 耗時操作1
    for (int i = 0; i < 10000; i++) { // 500
        NSLog(@"%i ==== %@", i, [NSThread currentThread]);
    }
    NSLog(@"++++++++++++++++++++++++++++++++++++++");
    if (self.isCancelled) {
        return;
    }

    // 耗時操作2
    for (int i = 0; i < 10000; i++) { // 500
        NSLog(@"%i ==== %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) {
        return;
    }
    NSLog(@"++++++++++++++++++++++++++++++++++++++");
    // 好所操作3
    for (int i = 0; i < 10000; i++) { // 500
        NSLog(@"%i ==== %@", i, [NSThread currentThread]);
    }
}
  • 隊列之間的依賴
    • 在操作添加到隊列之前, 利用操作調(diào)用addDependency, 就快要添加依賴
    • 添加依賴之后, 只有所有依賴的任務(wù)都執(zhí)行完畢, 才會執(zhí)行當前任務(wù)
    • 注意點: 不要相互依賴
    • 特點: 跨隊列依賴(GCD默認是不支持)
 // 3.添加依賴
    [op5 addDependency:op1];
    [op5 addDependency:op2];
    [op5 addDependency:op3];
    [op5 addDependency:op4];
  • 操作的監(jiān)聽

    • 只需要利用操作調(diào)用completionBlock即可
    • 只要任務(wù)執(zhí)行完畢, 就會回調(diào)completionBlock
  • 線程間的通信

    • 將任務(wù)添加到自己創(chuàng)建的隊列中
    • 再利用mainQueue回到主隊列
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末薪棒,一起剝皮案震驚了整個濱河市手蝎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌俐芯,老刑警劉巖棵介,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吧史,居然都是意外死亡邮辽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門贸营,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逆巍,“玉大人,你說我怎么就攤上這事莽使。” “怎么了笙僚?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵芳肌,是天一觀的道長。 經(jīng)常有香客問我肋层,道長亿笤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任栋猖,我火速辦了婚禮净薛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒲拉。我一直安慰自己肃拜,他們只是感情好痴腌,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著燃领,像睡著了一般士聪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猛蔽,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天剥悟,我揣著相機與錄音,去河邊找鬼曼库。 笑死区岗,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的毁枯。 我是一名探鬼主播慈缔,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼后众!你這毒婦竟也來了胀糜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蒂誉,失蹤者是張志新(化名)和其女友劉穎教藻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體右锨,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡括堤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了绍移。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悄窃。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹂窖,靈堂內(nèi)的尸體忽然破棺而出轧抗,到底是詐尸還是另有隱情,我是刑警寧澤瞬测,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布横媚,位于F島的核電站,受9級特大地震影響月趟,放射性物質(zhì)發(fā)生泄漏灯蝴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一孝宗、第九天 我趴在偏房一處隱蔽的房頂上張望穷躁。 院中可真熱鬧,春花似錦因妇、人聲如沸问潭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睦授。三九已至两芳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間去枷,已是汗流浹背怖辆。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留删顶,地道東北人竖螃。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像逗余,于是被迫代替她去往敵國和親特咆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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