iOS多線程那點事兒

前言


最近一直在做項目,也是經(jīng)常有用到一些線程的知識俄烁,抽出一些時間來對線程做一下匯總页屠,由淺入深蓖柔,也很方便新手入門况鸣。

概述


多線程開發(fā)在iOS中有著舉足輕重的位置镐捧,學(xué)習(xí)好多線程是每一個iOS Developer必須要掌握的技能。

概念


進(jìn)程

  • 進(jìn)程代表當(dāng)前運行的一個程序

  • 是系統(tǒng)分配資源的基本單位

  • 每個進(jìn)程之間是獨立的速址,每個進(jìn)程均運行在其專用且受保護(hù)的內(nèi)存空間內(nèi)

  • 比如同時打開QQ芍锚、Xcode并炮,系統(tǒng)就會分別啟動2個進(jìn)程

  • 進(jìn)程可以理解為一個工廠
  • 通過“活動監(jiān)視器”可以查看Mac系統(tǒng)中所開啟的進(jìn)程

線程

  • 線程是進(jìn)程的基本執(zhí)行單元逃魄,一個進(jìn)程(程序)的所有任務(wù)都在線程中執(zhí)行

  • 一個進(jìn)程含有一個線程或多個線程

  • 應(yīng)用程序打開后會默認(rèn)開辟一個線程叫做主線程或者UI線程

  • 比如使用酷狗播放音樂伍俘、使用迅雷下載電影勉躺,都需要在線程中執(zhí)行

  • 線程可以理解為工廠里的工人

串行

  • 多個任務(wù)按順序執(zhí)行

  • 類似于一個窗口辦公排隊

  • 也就是說饵溅,在同一時間內(nèi),1個線程只能執(zhí)行1個任務(wù)

  • 比如在1個線程中下載3個文件(分別是文件A蜕企、文件B幸乒、文件C)就要依次執(zhí)行

并行

  • 多個任務(wù)同一時間一起執(zhí)行

  • 類似于多個窗口辦公

  • 比如同時開啟3條線程分別下載3個文件(分別是文件A逝变、文件B、文件C)弥臼,同時執(zhí)行

并發(fā)

  • 很多人容易認(rèn)為并發(fā)和并行是一個意思径缅,但實際上他們有本質(zhì)的區(qū)別

  • 并發(fā)看起來像多個任務(wù)同一時間一起執(zhí)行

  • 但實際上是CPU快速的輪轉(zhuǎn)切換造成的假象

多線程

  • 本質(zhì)

    • 在一個進(jìn)程中開啟多個線程并發(fā)執(zhí)行
  • 原理

    • 同一時間纳猪,CPU只能處理1條線程氏堤,只有1條線程在工作(執(zhí)行)

    • 多線程并發(fā)(同時)執(zhí)行鼠锈,其實是CPU快速地在多條線程之間調(diào)度(切換)

    • 如果CPU調(diào)度線程的時間足夠快购笆,就造成了多線程并發(fā)執(zhí)行的假象

  • 優(yōu)點

    • 能適當(dāng)提高程序的執(zhí)行效率

    • 能適當(dāng)提高資源利用率(CPU样傍、內(nèi)存利用率)

  • 缺點

    • 線程需要耗費系統(tǒng)資源

    • 主線程需要消耗椘趟欤空間的1MB資源

    • 其他線程每個消耗512KB資源

    • 程序設(shè)計更加復(fù)雜:比如線程之間的通信娃循、多線程的數(shù)據(jù)共享

不推薦過多使用

主線程

  • 概念
    • 一個iOS程序運行后捌斧,默認(rèn)會開啟1條線程捞蚂,稱為“主線程”或“UI線程”
  • 作用
    • 顯示\刷新UI界面

    • 處理UI事件(比如點擊事件、滾動事件敲霍、拖拽事件等)

  • 注意
    • 別將比較耗時的操作放到主線程中

    • 耗時操作會卡住主線程肩杈,嚴(yán)重影響UI的流暢度扩然,給用戶一種“卡”的壞體驗

耗時操作執(zhí)行

  • 如果放在主線程
  • 因為在主線程中的任務(wù)是按照順序依次執(zhí)行的

  • 如果把耗時操作放在主線程里夫偶,會等待它執(zhí)行完后才能執(zhí)行其他操作

  • 如果在等待執(zhí)行完畢的時間里點擊了其他控件就會給用戶一種卡住的感覺兵拢,嚴(yán)重影響用戶體驗


  • 如果放在子線程
  • 在用戶點擊按鈕的時候就會做出反應(yīng)

  • 兩個線程同時執(zhí)行说铃,互不影響

多線程的實現(xiàn)


方案

PThread

  • 簡單了解即可
- (IBAction)buttonClick:(id)sender {
    pthread_t thread;
    pthread_create(&thread, NULL, run, NULL);

    pthread_t thread2;
    pthread_create(&thread2, NULL, run, NULL);
}

void * run(void *param)
{
    for (NSInteger i = 0; i<50000; i++) {
        NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);
    }
    return NULL;
}

NSThread

  • 基本創(chuàng)建方法

  • 一個NSThread對象就代表一條線程

  • 創(chuàng)建截汪、啟動線程

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction) object:nil];

// 需要手動開啟線程
[thread start];
  • 主線程相關(guān)用法
// 獲得主線程
+ (NSThread *)mainThread;

// 是否為主線程
- (BOOL)isMainThread;

// 是否為主線程
+ (BOOL)isMainThread;
  • 獲得當(dāng)前線程
NSThread *current = [NSThread currentThread];
  • 線程的名字
- (void)setName:(NSString *)name;
- (NSString *)name;
  • 其他創(chuàng)建方法

    • 創(chuàng)建線程后自動啟動線程

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


- 隱式創(chuàng)建并啟動線程

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


- 上述2種創(chuàng)建線程方式的優(yōu)缺點

- 優(yōu)點:簡單快捷

- 缺點:無法對線程進(jìn)行更詳細(xì)的設(shè)置

- 線程睡眠

[NSThread sleepForTimeInterval:2]; // 讓線程睡眠2秒(阻塞2秒)

[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];

- 退出當(dāng)前線程

[NSThread exit];

- 設(shè)置線程優(yōu)先級 (默認(rèn)0.5)

thread.threadPriority = 1.0f;


####GCD

- 這里寫了關(guān)于GCD的詳細(xì)介紹疾牲,包括GCD死鎖等問題。

    - http://www.reibang.com/p/f13c4b336d34

#### NSOperation

- NSOperation 是蘋果公司對 GCD 的封裝衙解,完全面向?qū)ο笱羧幔允褂闷饋砀美斫狻?大家可以看到 NSOperation 和 NSOperationQueue 分別對應(yīng) GCD 的 任務(wù) 和 隊列 。操作步驟也很好理解蚓峦。

- NSOperation也有兩個概念舌剂,隊列和任務(wù)。
- 主隊列
    - [NSOperationQueue mainQueue]
    - 凡是添加到主隊列中的任務(wù)(NSOperation)暑椰,都會放到主線程中執(zhí)行
- 非主隊列(其他隊列)
    - [[NSOperationQueue alloc] init]
    - 同時包含了:串行霍转、并發(fā)功能
    - 添加到這種隊列中的任務(wù)(NSOperation)避消,就會自動放到子線程中執(zhí)行


- 系統(tǒng)為我們提供了NSOperation的子類我們可以直接使用
- 當(dāng)某個任務(wù)經(jīng)常使用,我們可以自定義NSOperation,在這個NSOperation中的main方法中寫任務(wù)。
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction) object:nil];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"haha ----- %@", [NSThread currentThread]);
}];

// 隊列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];

// 任務(wù)1完事以后才執(zhí)行任務(wù)2
[operation2 addDependency:operation1];

// 設(shè)置最大并發(fā)數(shù)()
operationQueue.maxConcurrentOperationCount = 4;

[operationQueue addOperationWithBlock:^{
    NSLog(@"hello ------ %@", [NSThread currentThread]);
}];

[operationQueue addOperation:operation1];
[operationQueue addOperation:operation2];

#### NSOperation 對比 GCD

- GCD效率更高,使用起來也很方便

- NSOperation面向?qū)ο螅勺x性更高,架構(gòu)更清晰,對于復(fù)雜多線程場景旁瘫,如并發(fā)中存在串行宁仔,和設(shè)置最大并發(fā)數(shù),擁有現(xiàn)在的API,使用起來特別簡單

# 線程的狀態(tài)
-----

![](http://upload-images.jianshu.io/upload_images/2595997-c99d3dd0bb01ffe4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#### 控制線程的狀態(tài)
------

- 啟動線程

// 進(jìn)入就緒狀態(tài)->運行狀態(tài)。 當(dāng)線程執(zhí)行完畢自動進(jìn)入死亡狀態(tài)。

  • (void)start;

- 阻塞(暫停)線程

// 進(jìn)入阻塞狀態(tài)

  • (void)sleepUntilData:(NSDate *)data;
  • (void)sleepForTimeInterval:(NSTimeInterval)ti;

- 強制停止?fàn)顟B(tài)

// 進(jìn)入死亡狀態(tài)

  • (void)exit;

- 注意
  - 一旦線程停止(死亡)了,就不能再次開啟任務(wù)

#### 多線程的安全隱患問題
-------
- 1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源

- 比如多個線程訪問同一個對象、同一個變量、同一個文件

- 當(dāng)多個線程訪問同一塊資源時脯倚,很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全問題

- 比如下面這個例子

![](http://upload-images.jianshu.io/upload_images/2595997-ad279c08b04af078.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 線程A從內(nèi)存中拿出一個Integer類型的值再沧,為17炒瘸;進(jìn)行加1操作后變?yōu)?8隘截,然后返回給內(nèi)存

- 線程B同時從內(nèi)存中拿出一個Integer類型的值统台,為17;進(jìn)行加1操作后變?yōu)?8,然后返回給內(nèi)存

- 出現(xiàn)的問題就是分別在兩個線程中做了加1操作,然而最后的結(jié)果只顯示了一次加1的結(jié)果,出現(xiàn)了數(shù)據(jù)錯亂的問題,正確結(jié)果應(yīng)該是變?yōu)?0

-------

- 解決方案,使用互斥鎖

![](http://upload-images.jianshu.io/upload_images/2595997-7a4df7f138bec881.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 線程A進(jìn)入內(nèi)存讀取值得時候先加一把鎖庐镐,讓外界無法拿到17進(jìn)行修改,等線程A對17做完加1操作后返回給內(nèi)存后,再解鎖.

- 此時如果線程B來內(nèi)存中想要修改17的時候,發(fā)現(xiàn)上了鎖,只能等待線程A做完操作后才能修改值嚷那,而A操作完后此時的值已經(jīng)變成了18队询,B從內(nèi)存中要修改的話,直接從內(nèi)存中拿到的就是18范嘱,開始修改送膳,然后加鎖不讓其他線程進(jìn)來晒奕。改完過后坑律,在解鎖宫屠。方便下一個線程進(jìn)來修改。

#### 互斥鎖
------

- 使用前提
  - 多條線程搶奪同一塊資源
- 相關(guān)專業(yè)術(shù)語
  - 線程同步
- 互斥鎖使用格式

@synchronized(鎖對象)
{
//需要鎖定的代碼
}


- 注意:
  - 鎖定1份代碼只用一把鎖灿渴,用多把鎖是無效的

  - 為了保證唯一性,鎖對象一般填self
- 互斥鎖的優(yōu)缺點
  - 優(yōu)點:
    - 能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
  - 缺點
    - 需要消耗大量的CPU資源


####線程間通信
-----
- 概念
  - 在1個進(jìn)程中,線程往往不是孤立存在的,多個線程之間需要經(jīng)常進(jìn)行通信
- 表現(xiàn)
  - 1個線程傳遞數(shù)據(jù)給另1個線程

  - 在1個線程中執(zhí)行完特定任務(wù)后,轉(zhuǎn)到另1個線程繼續(xù)執(zhí)行任務(wù)
- 線程間通信常用方法

  • (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
  • (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait;

- 例子

    ![](http://upload-images.jianshu.io/upload_images/2595997-b62b48d63d07cccf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    - 在子線程中做耗時的操作船响,比如下載圖片

    - 在子線程中操作完后要回到主線程做UI的刷新操作(顯示圖片)

- 另外一種線程通信方法(利用NSPort)
  - 如果子線程想要傳數(shù)據(jù)給主線程,主線程就要返回一個Port對象讓子線程去擁有犹褒,子線程通過Port對象對主線程進(jìn)行操作。

![](http://upload-images.jianshu.io/upload_images/2595997-2e137ee0e5e71421.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
   
# 心靈雞湯
------
禪師問:你覺得是一錠金子好诅病,還是一堆爛泥好呢埋涧?壯士答:當(dāng)然是金子啊郑叠!禪師笑曰:假如你是一顆種子呢?壯士答:別他媽搞笑了嘁傀,這錠金子我要定了蕾殴,快給我松手,松手啊魂淡!郁稍!

難受的時候摸摸自己的胸靡羡,告訴自己是個漢子,要堅強~

![](http://upload-images.jianshu.io/upload_images/2595997-51f8a282f95b7f8c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卒落,一起剝皮案震驚了整個濱河市岔帽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌导绷,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屎飘,死亡現(xiàn)場離奇詭異妥曲,居然都是意外死亡,警方通過查閱死者的電腦和手機钦购,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門檐盟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人押桃,你說我怎么就攤上這事葵萎。” “怎么了唱凯?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵羡忘,是天一觀的道長。 經(jīng)常有香客問我磕昼,道長卷雕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任票从,我火速辦了婚禮漫雕,結(jié)果婚禮上滨嘱,老公的妹妹穿的比我還像新娘。我一直安慰自己浸间,他們只是感情好太雨,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魁蒜,像睡著了一般囊扳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梅惯,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天宪拥,我揣著相機與錄音,去河邊找鬼铣减。 笑死她君,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的葫哗。 我是一名探鬼主播缔刹,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劣针!你這毒婦竟也來了校镐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤捺典,失蹤者是張志新(化名)和其女友劉穎鸟廓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體襟己,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡引谜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了擎浴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片员咽。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贮预,靈堂內(nèi)的尸體忽然破棺而出贝室,到底是詐尸還是另有隱情,我是刑警寧澤仿吞,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布滑频,位于F島的核電站,受9級特大地震影響唤冈,放射性物質(zhì)發(fā)生泄漏误趴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一务傲、第九天 我趴在偏房一處隱蔽的房頂上張望凉当。 院中可真熱鬧枣申,春花似錦、人聲如沸看杭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楼雹。三九已至模孩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贮缅,已是汗流浹背榨咐。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谴供,地道東北人块茁。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像桂肌,于是被迫代替她去往敵國和親数焊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

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

  • Object C中創(chuàng)建線程的方法是什么崎场?如果在主線程中執(zhí)行代碼佩耳,方法是什么?如果想延時執(zhí)行代碼谭跨、方法又是什么干厚? 1...
    AlanGe閱讀 1,736評論 0 17
  • 什么是進(jìn)程? 進(jìn)程是指在系統(tǒng)中正在運行的一個應(yīng)用程序螃宙。 每個進(jìn)程之間是獨立的萍诱,每個進(jìn)程均運行在其專用且受保護(hù)的內(nèi)存...
    珍此良辰閱讀 1,255評論 1 5
  • 一周瞎、基本概念 線程是用來執(zhí)行任務(wù)的苗缩,線程徹底執(zhí)行完任務(wù)A才能執(zhí)行任務(wù)B,為了同時執(zhí)行兩個任務(wù)声诸,產(chǎn)生了多線程 1酱讶、進(jìn)...
    空白Null閱讀 676評論 0 3
  • 真是一肚子火,沒地方撒彼乌。 與我何干泻肯。
    天風(fēng)zz閱讀 184評論 0 0
  • 現(xiàn)在的主力機是 Surface Book渊迁,雖然硬件優(yōu)秀,但軟件差強人意并不是心中的 「The ultimate l...
    Kenso閱讀 274評論 0 0