iOS 多線程NSThread

一荞彼、什么是多線程

  • 多線程是指從軟件或者硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)恤左。
  • 由于一條線程同一時間只能處理一個任務(wù),所以線程里的任務(wù)必須按順序執(zhí)行遂鹊。
  • 如果遇到耗時操作(網(wǎng)絡(luò)通信、耗時計算蔗包、音樂播放等)秉扑,等上一個操作完成再執(zhí)行下一個任務(wù),在此段時間內(nèi)调限,用戶得不到任何響應(yīng)舟陆,這個體驗無疑是極糟的。
  • 因此耻矮,在iOS編程中秦躯,通常將耗時操作單獨放在一個線程中,而把用戶交互的操作放在主線程中淘钟。

二宦赠、進(jìn)程與線程

  • 進(jìn)程:
進(jìn)程是指系統(tǒng)中正在運行的應(yīng)用程序陪毡。這個'運行中的程序'就是一個進(jìn)程米母。
每個進(jìn)程都擁有著自己的地址空間勾扭。

進(jìn)程有3個主要特征:

獨立性:  
進(jìn)程是一個能夠獨立運行的基本單位,它既擁有自己獨立的資源铁瞒,又擁有著自己私有的地址空間妙色。
在沒有經(jīng)過進(jìn)程本身允許的情況下,一個用戶的進(jìn)程是不可以直接訪問其他進(jìn)程的地址空間的慧耍。 

動態(tài)性:    
進(jìn)程的實質(zhì)是程序在系統(tǒng)中的一次執(zhí)行過程身辨。
程序只是一個靜態(tài)的指令集合,而進(jìn)程是一個正在系統(tǒng)中活動的指令集合芍碧。
在進(jìn)程中加入了時間的概念煌珊,它就具有了自己的生命周期和各自不同的狀態(tài),進(jìn)程是動態(tài)消亡的泌豆。
  
并發(fā)性:  
多個進(jìn)程可以在單個處理器中同時進(jìn)行定庵,而不會相互影響。

  • 線程:
多線程擴展了多進(jìn)程的概念踪危,使得同一進(jìn)程可以同時并發(fā)處理多個任務(wù)蔬浙。
一個程序的運行至少需要一個線程,一個進(jìn)程想要執(zhí)行任務(wù)贞远,也必須要有至少一個線程畴博,而這個線程就被稱作主線程。 
通常來說蓝仲,只有一個主線程俱病。
當(dāng)進(jìn)程被初始化后,主線程就被創(chuàng)建了袱结,主線程是其他線程最終的父線程亮隙,所有界面的顯示操作必須在主線程進(jìn)行。

三擎勘、多線程的優(yōu)勢

  1. 進(jìn)程間不能共享內(nèi)存咱揍,但線程之間的共享內(nèi)存是很容易的。

  2. 當(dāng)硬件處理器的數(shù)量有所增加時,程序運行的速度更快递递,無需做其他調(diào)整盏求。

  3. 充分發(fā)揮了多核處理器的優(yōu)勢,將不同的任務(wù)分配給不同的處理器硼砰,真正進(jìn)入“并行運算”的狀態(tài)。

  4. 將耗時欣硼、并發(fā)需求高的任務(wù)分配到其他線程執(zhí)行题翰,而主線程則負(fù)責(zé)統(tǒng)一更新界面,這樣使得應(yīng)用程序更加流暢,用戶體驗更好豹障。

四冯事、多線程的劣勢

  1. 開啟線程需要占用一定的內(nèi)存空間[默認(rèn)情況下,主線程最大占用1M的棧區(qū)空間血公、子線程最大占用512K的棧區(qū)空間]昵仅,如果要開啟大量的線程,勢必會占用大量的內(nèi)存空間累魔,從而降低程序的性能摔笤。
  2. 開啟的線程越多,CPU在調(diào)度線程上的開銷就越大垦写,一般最好不要同時開啟超過5個線程吕世。
  3. 程序的設(shè)計會變得更加復(fù)雜,如線程之間的通信梯投、多線程間的數(shù)據(jù)共享等命辖。

五、線程的串行和并行

  • 串行:
如果在一個進(jìn)程中只有一個線程晚伙,而這個進(jìn)程要執(zhí)行多個任務(wù)吮龄,
那么這個線程只能一個一個的按照順序執(zhí)行這些任務(wù),也就是說咆疗,
在同一時間內(nèi)漓帚,一個線程只能執(zhí)行一個任務(wù),這樣的線程執(zhí)行方式稱為線程的串行午磁。
  • 并行:
如果一個進(jìn)程中包含的線程不止一條尝抖,
每條線程之間可以并行執(zhí)行不同的任務(wù),這叫做線程的并行迅皇,也叫多線程昧辽。
  • 補充:
假如一個進(jìn)程有3個線程,每個線程執(zhí)行1個任務(wù)登颓,3個下載任務(wù)沒有先后順序搅荞。可以同時執(zhí)行框咙。 
同一時間咕痛,CPU只能處理一個線程,也就是只有一個線程在工作喇嘱。
由于CPU快速的在多個線程之間調(diào)度茉贡,人眼無法察覺到,就造成了多線程并發(fā)執(zhí)行的假象者铜。

六腔丧、線程的狀態(tài)

當(dāng)線程被創(chuàng)建并啟動之后放椰,既不是一啟動就進(jìn)入執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)愉粤,即便程序啟動運行之后砾医,它也不可能一直占用CPU獨自運行。

由于CPU需要在多個線程之間進(jìn)行切換科汗,造成了線程的狀態(tài)會在多次運行藻烤、就緒之間進(jìn)行切換绷雏。

線程的狀態(tài)主要有5個:

1. 新建New        
當(dāng)程序新建一個線程之后头滔,該線程就處于新建狀態(tài)。
和其他對象一樣涎显,只是由系統(tǒng)分配了內(nèi)存坤检,并初始化了內(nèi)部成員變量的值。
此時的線程沒有任何動態(tài)特征

2. 就緒Runable
當(dāng)線程被start之后期吓,該線程就處于就緒狀態(tài)早歇。
系統(tǒng)會為其創(chuàng)建 方法調(diào)用的棧和 程序計數(shù)器。

3. 運行Running
當(dāng)CPU調(diào)度當(dāng)前線程的時候讨勤,將其他線程掛起箭跳,當(dāng)前線程變?yōu)檫\行狀態(tài)。
當(dāng)CPU調(diào)度其他線程時潭千,當(dāng)前線程回到就緒狀態(tài)谱姓。
測試線程是否在運行,調(diào)用isExecuting方法刨晴,若返回YES屉来,則處于運行狀態(tài)。

4. 終止Exit
* 線程執(zhí)行方法執(zhí)行完成狈癞,線程正常結(jié)束
* 線程執(zhí)行的過程出現(xiàn)異常茄靠,線程崩潰結(jié)束
* 直接調(diào)用NSThread類的exit方法,終止當(dāng)前正在運行的線程
* 測試線程是否結(jié)束蝶桶,調(diào)用isFinished方法判斷慨绳,若返回YES,則已終止真竖。

5. 阻塞Blocked    
如果當(dāng)前正在執(zhí)行的線程需要暫停一段時間脐雪,并進(jìn)入阻塞狀態(tài),通過NSThread類的兩個方法:

//讓當(dāng)前執(zhí)行的線程暫停到date參數(shù)代表的時間疼邀,并且進(jìn)入阻塞狀態(tài)
+ (void)sleepUntilDate:(NSDate *)date;

//讓正在執(zhí)行的線程暫停ti秒喂江,并進(jìn)入阻塞狀態(tài)
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

七、線程間的安全隱患

進(jìn)程中的一塊資源可能會被多個線程共享旁振,也就是多個線程可能訪問同一塊資源获询,這里的資源包括對象涨岁、變量文件等吉嚣。
當(dāng)多個線程同時訪問同一塊資源時梢薪,會造成資源搶奪,很容易引發(fā)數(shù)據(jù)錯亂數(shù)據(jù)安全的問題尝哆。
為了解決這個問題秉撇,實現(xiàn)數(shù)據(jù)的安全訪問,可以使用線程間的加鎖秋泄。

  • 數(shù)據(jù)混亂示例:
/** 售票處理 */
- (void)saleTickets{
    while (true) {
        //模擬延時
        [NSThread sleepForTimeInterval:1.0];
        
        //判斷是否還有票
        if (self.leftTickets > 0) {
            self.leftTickets--;
            
            NSLog(@"%@賣了一張票琐馆,還剩下%lu張票",[NSThread currentThread].name,self.leftTickets);
        } else{
            NSLog(@"票已售完");
            return;
        }
    }
}


-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //設(shè)定總票數(shù)
    self.leftTickets = 100;
    //創(chuàng)建3個線程,啟動后執(zhí)行saleTickets方法賣票
    NSThread *t1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t1.name = @"1號窗口";
    [t1 start];
    
    NSThread *t2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t2.name = @"2號窗口";
    [t2 start];
    
    NSThread *t3 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t3.name = @"3號窗口";
    [t3 start];
}
未加鎖.png

可以看到恒序,由于3個線程的并發(fā)操作瘦麸,同一時間搶奪一個資源leftTickets,造成了剩余票數(shù)統(tǒng)計的混亂歧胁。

  • 加鎖示例:
@synchronized (obj)
{
    //插入被修飾的代碼塊
}
  • 使用同步鎖的時候滋饲,要盡量讓同步代碼塊包圍的代碼范圍最小,而且要鎖定共享資源的全部讀寫部分的代碼喊巍。

obj就是加鎖對象屠缭,添加了鎖對象后,鎖對象實現(xiàn)了對多線程的監(jiān)管崭参,保證同一時刻只有一個線程執(zhí)行呵曹,當(dāng)同步代碼塊執(zhí)行完成后,鎖定對象就會釋放同步監(jiān)視器的鎖定阵翎。

/** 售票處理 */
- (void)saleTickets{
    while (true) {
        //模擬延時
        [NSThread sleepForTimeInterval:1.0];
        
        //判斷是否還有票
        @synchronized(self){
            if (self.leftTickets > 0) {
                self.leftTickets--;
                
                NSLog(@"%@賣了一張票逢并,還剩下%lu張票",[NSThread currentThread].name,self.leftTickets);
            } else{
                NSLog(@"票已售完");
                return;
            }
        }
    }
}
加鎖.png

線程添加同步鎖后,實現(xiàn)了線程的同步運行郭卫,也就是說砍聊,使多線程按順序執(zhí)行任務(wù)。需要注意的是贰军,同步鎖會消耗大量的CPU資源玻蝌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市词疼,隨后出現(xiàn)的幾起案子俯树,更是在濱河造成了極大的恐慌,老刑警劉巖贰盗,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件许饿,死亡現(xiàn)場離奇詭異,居然都是意外死亡舵盈,警方通過查閱死者的電腦和手機陋率,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門球化,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓦糟,你說我怎么就攤上這事筒愚。” “怎么了菩浙?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵巢掺,是天一觀的道長。 經(jīng)常有香客問我劲蜻,道長陆淀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任斋竞,我火速辦了婚禮倔约,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坝初。我一直安慰自己,他們只是感情好钾军,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布鳄袍。 她就那樣靜靜地躺著,像睡著了一般吏恭。 火紅的嫁衣襯著肌膚如雪拗小。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天樱哼,我揣著相機與錄音哀九,去河邊找鬼。 笑死搅幅,一個胖子當(dāng)著我的面吹牛阅束,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茄唐,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼息裸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沪编?” 一聲冷哼從身側(cè)響起呼盆,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚁廓,沒想到半個月后访圃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡相嵌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年腿时,在試婚紗的時候發(fā)現(xiàn)自己被綠了克胳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡圈匆,死狀恐怖漠另,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情跃赚,我是刑警寧澤笆搓,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站纬傲,受9級特大地震影響满败,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叹括,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一算墨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汁雷,春花似錦净嘀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厢漩,卻和暖如春膜眠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溜嗜。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工宵膨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炸宵。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓辟躏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親焙压。 傳聞我的和親對象是個殘疾皇子鸿脓,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

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

  • 又來到了一個老生常談的問題,應(yīng)用層軟件開發(fā)的程序員要不要了解和深入學(xué)習(xí)操作系統(tǒng)呢涯曲? 今天就這個問題開始野哭,來談?wù)劜?..
    tangsl閱讀 4,129評論 0 23
  • Object C中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼幻件,方法是什么拨黔?如果想延時執(zhí)行代碼、方法又是什么绰沥? 1...
    AlanGe閱讀 1,740評論 0 17
  • 第三章 Java內(nèi)存模型 3.1 Java內(nèi)存模型的基礎(chǔ) 通信在共享內(nèi)存的模型里篱蝇,通過寫-讀內(nèi)存中的公共狀態(tài)進(jìn)行隱...
    澤毛閱讀 4,355評論 2 22
  • Java-Review-Note——4.多線程 標(biāo)簽: JavaStudy PS:本來是分開三篇的贺待,后來想想還是整...
    coder_pig閱讀 1,653評論 2 17
  • 親愛的兒子 你就月份就要開學(xué)了。但是現(xiàn)在每天媽媽布置給你的作業(yè)你依舊很不情愿的做零截。 媽媽希望隨著你的長大麸塞。您能夠?qū)W...
    我是小小彥閱讀 158評論 0 1