多線程(2)——NSThread

iOS中實(shí)現(xiàn)多線程的四種方案

  • pthread
  • NSThread
  • GCD
  • NSOpreation

Pthread:這種純c語(yǔ)言的就別用了吧,還要自己管理線程的生命周期,果斷不用
NSThread:這個(gè)雖然自己管理線程的生命周期,但這是面向?qū)ο蟮?,OC語(yǔ)言,不常用到但要會(huì)用
GCD:這個(gè)很重要、很重要、很重要 自動(dòng)管理線程的生命周期 使用率非常非常頻繁
NSOpreation:這個(gè)也很重要,必須掌握,沒什么好說(shuō)的,也是自動(dòng)管理線程的生命周期,使用率也非常高

簡(jiǎn)單了解下Pthread

只要create一次就會(huì)創(chuàng)建一個(gè)新的線程
系統(tǒng)會(huì)自動(dòng)在子線程中調(diào)用傳入的函數(shù)

  /*
第一個(gè)參數(shù): 線程的代號(hào)(當(dāng)做就是線程)
第二個(gè)參數(shù): 線程的屬性
第三個(gè)參數(shù): 指向函數(shù)的指針, 就是將來(lái)線程需要執(zhí)行的方法
第四個(gè)參數(shù): 給第三個(gè)參數(shù)的指向函數(shù)的指針 傳遞的參數(shù)
一般情況下C語(yǔ)言中的類型都是以 _t或者Ref結(jié)尾
  */
pthread_t threadId;
// 只要create一次就會(huì)創(chuàng)建一個(gè)新的線程
pthread_create(&threadId , NULL, &demo, "hq");

關(guān)于NSThread

  • NSThread對(duì)比后面兩種是相對(duì)輕量級(jí)的,更直觀地控制線程對(duì)象
  • 但需要自己管理線程的生命周期、同步、加鎖問題,在性能上會(huì)有一定的消耗
  • 注意:一個(gè)NSThread對(duì)象就代表一條線程

創(chuàng)建方法

** 第一種方法 ** (手動(dòng)開啟線程)

// 線程一啟動(dòng),就會(huì)在線程thread中執(zhí)行self的run方法

// 1.創(chuàng)建線程
NSThread *thread = [[NSThread alloc] initWithTarget:selfselector:@selector(run) object:nil];

// 2.設(shè)置線程的優(yōu)先級(jí)(0.0 - 1.0,1.0最高級(jí))  
thread.threadPriority = 1; 

// 3.啟動(dòng)線程
[thread start];

** 第二種方法 ** (自動(dòng)開啟線程)

// detachNewThreadSelector: 不用手動(dòng)調(diào)用start方法, 系統(tǒng)會(huì)自動(dòng)啟動(dòng) 沒有返回值, 不能對(duì)線程進(jìn)行更多的設(shè)置

[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

** 第三種方法 ** (隱式創(chuàng)建并啟動(dòng)線程)

// 系統(tǒng)就會(huì)自動(dòng)創(chuàng)建一個(gè)子線程, 并且在子線程中自動(dòng)執(zhí)行self的@selector方法

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

NSThread用法

  • 主線程相關(guān)用法
 + (NSThread *)mainThread; // 獲得主線程
- (BOOL)isMainThread; // 是否為主線程
+ (BOOL)isMainThread; // 是否為主線程
NSThread *main = [NSThread mainThread];
  • 獲得當(dāng)前線程
NSThread *current = [NSThread currentThread];
  • 獲取線程的名字

    - (void)setName:(NSString *)name;
    - (NSString *)name;
    

線程的狀態(tài)

  • 創(chuàng)建出來(lái) -> 新建狀態(tài)
  • 調(diào)用start -> 準(zhǔn)備就緒
  • 被CPU調(diào)用 -> 運(yùn)行
  • sleep -> 阻塞
  • 執(zhí)行完畢, 或者被強(qiáng)制關(guān)閉 -> 死亡
啟動(dòng)線程
- (void)start; 
// 進(jìn)入就緒狀態(tài) -> 運(yùn)行狀態(tài)。當(dāng)線程任務(wù)執(zhí)行完畢,自動(dòng)進(jìn)入死亡狀態(tài)

阻塞(暫停)線程
// sleep方法是一個(gè)類方法, 所以說(shuō)明在哪個(gè)線程中調(diào)用, 就會(huì)給哪個(gè)線程設(shè)置暫時(shí)
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)time;

// 暫停1s  
[NSThread sleepForTimeInterval:1]; 
[NSThread sleepUntilDate:[NSDate dateWithTimeInterval:1 sinceDate:[NSDate date]]];
強(qiáng)制停止線程
// 進(jìn)入死亡狀態(tài)
+ (void)exit;
注意:一旦線程停止(死亡)了,就不能再次開啟任務(wù)

多線程的安全隱患及解決措施

  • 資源共享
    • 一塊資源可能會(huì)被多個(gè)線程共享,也就是多個(gè)線程可能會(huì)訪問同一塊資源,比如多個(gè)線程訪問同一個(gè)對(duì)象、同一個(gè)變量、同一個(gè)文件等
    • 當(dāng)多個(gè)線程訪問同一塊資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問題
  • 解決措施
    • 互斥鎖
    • 互斥鎖的使用前提:多條線程搶奪同一塊資源
    • 注意:鎖定1份代碼只用1把鎖,用多把鎖是無(wú)效的
  • 使用格式
// (鎖對(duì)象self)
@synchronized
{ 
    // 需要鎖定的代碼      
}
        /*
         只要被@synchronized的{}包裹起來(lái)的代碼, 同一時(shí)刻就只能被一個(gè)線程執(zhí)行
         注意:
         1. 只要枷鎖就會(huì)消耗性能
         2. 加鎖必須傳遞一個(gè)對(duì)象, 作為鎖
         3. 如果想真正的鎖住代碼, 那么多個(gè)線程必須使用同一把鎖才行
         4. 加鎖的時(shí)候盡量縮小范圍, 因?yàn)榉秶酱笮阅芫驮降?         */
  • 互斥鎖的優(yōu)缺點(diǎn)

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

    • 線程同步的意思是:多條線程在同一條線上執(zhí)行(按順序地執(zhí)行任務(wù))
      互斥鎖,就是使用了線程同步技術(shù)

原子和非原子屬性

  • OC在定義屬性時(shí)有nonatomic和atomic兩種選擇
    • atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic)
    • nonatomic:非原子屬性,不會(huì)為setter方法加鎖
  • 原子和非原子屬性的選擇
    • atomic:線程安全,需要消耗大量的資源
    • nonatomic:非線程安全,適合內(nèi)存小的移動(dòng)設(shè)備
  • iOS開發(fā)的建議
    • 所有屬性都聲明為nonatomic
    • 盡量避免多線程搶奪同一塊資源
    • 盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減小移動(dòng)客戶端的壓力

注意點(diǎn): atomic系統(tǒng)自動(dòng)給我們添加的鎖不是互斥鎖/ 自旋鎖

  • 自旋鎖和互斥鎖對(duì)比
    • 相同點(diǎn):都能夠保證多線程在同一時(shí)候, 只能有一個(gè)線程操作鎖定的代碼
    • 不同點(diǎn)
      • 如果是互斥鎖, 假如現(xiàn)在被鎖住了, 那么后面來(lái)得線程就會(huì)進(jìn)入”休眠”狀態(tài), 直到解鎖之后, 又會(huì)喚醒線程繼續(xù)執(zhí)行
      • 如果是自旋鎖, 假如現(xiàn)在被鎖住了, 那么后面來(lái)得線程不會(huì)進(jìn)入休眠狀態(tài), 會(huì)一直傻傻的等待, 直到解鎖之后立刻執(zhí)行
      • 自旋鎖更適合做一些較短的操作

線程間的通信

  • 什么叫做線程間通信

    • 在1個(gè)進(jìn)程中,線程往往不是孤立存在的,多個(gè)線程之間需要經(jīng)常進(jìn)行通信
    • 比如在主線程添加imageView,在子線程中下載圖片,然后又回到主線程中顯示圖片
    • 注意點(diǎn): 更新UI一定要在主線程中更新
  • 線程間通信的體現(xiàn)

    • 1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
    • 在1個(gè)線程中執(zhí)行完特定任務(wù)后,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
  • 在當(dāng)前線程

[self performSelector:@selector(run) withObject:nil]; 
  • 在主線程

    [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
    
  • 在指定線程

[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];  

// waitUntilDone的含義: 如果傳入的是YES: 那么會(huì)等到主線程中的方法執(zhí)行完畢, 才會(huì)繼續(xù)執(zhí)行下面其他行的代碼 如果傳入的是NO: 那么不用等到主線程中的方法執(zhí)行完畢, 就可以繼續(xù)執(zhí)行下面其它行的代碼

線程間的通信最常見的就是在子線程中做耗時(shí)操作,然后回到主線程刷新UI

關(guān)于NSThread的總結(jié)

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

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

  • 一、多線程基礎(chǔ) 基本概念 進(jìn)程進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且...
    AlanGe閱讀 650評(píng)論 0 0
  • 多線程基本概念 單核CPU,同一時(shí)間cpu只能處理1個(gè)線程,只有1個(gè)線程在執(zhí)行 。多線程同時(shí)執(zhí)行:是CPU快速的在...
    WeiHing閱讀 786評(píng)論 1 5
  • 線程概述 有些程序是一條直線,起點(diǎn)到終點(diǎn);有些程序是一個(gè)圓,不斷循環(huán),直到將它切斷一個(gè)運(yùn)行著的程序就是一個(gè)進(jìn)程或者...
    褪而未變閱讀 358評(píng)論 0 0
  • 希望有一天你難過了,會(huì)第一個(gè)想起我的名字。
    436522a73fa0閱讀 266評(píng)論 0 1
  • 一、初相識(shí) 我和曉玥認(rèn)識(shí)的時(shí)候是在高一軍訓(xùn)的時(shí)候。我記得那時(shí)候是夏天吧,大家都穿著軍訓(xùn)服在訓(xùn)練場(chǎng)上跑步。因?yàn)槲易约?..
    我是影子啊閱讀 650評(píng)論 5 5

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