內(nèi)功心法-多線程的基本使用

一 ) 為什么使用多線程?

每個(gè)iOS應(yīng)用程序都有個(gè)專(zhuān)門(mén)用來(lái)更新顯示UI界面、處理用戶(hù)的觸摸事件的主線程,因此不能將其他太耗時(shí)的操作放在主線程中執(zhí)行,不然會(huì)造成主線程堵塞(出現(xiàn)卡機(jī)現(xiàn)象),帶來(lái)極壞的用戶(hù)體驗(yàn)。一般的解決方案就是將那些耗時(shí)的操作放到另外一個(gè)線程中去執(zhí)行,多線程編程是防止主線程堵塞,增加運(yùn)行效率的最佳方法。

這里有兩個(gè)概念是進(jìn)程和線程。 進(jìn)程就是負(fù)責(zé)程序運(yùn)行的內(nèi)存分配,而線程就是進(jìn)程中得一個(gè)獨(dú)立的執(zhí)行那個(gè)路徑。它是程序的執(zhí)行路徑,負(fù)責(zé)程序中代碼的實(shí)際運(yùn)行。這好比進(jìn)程就是火車(chē),線程就是火車(chē)的車(chē)廂,沒(méi)有火車(chē),車(chē)廂也跑不起來(lái)當(dāng)然一個(gè)火車(chē)也不能只有一個(gè)車(chē)廂。

二)? ios 中常用的三種多線程是什么?

1? Thread: 這是相對(duì)輕量級(jí)別的,抽象級(jí)別相對(duì)來(lái)說(shuō)比較低,但是需要管理線程的生命周期,同步以及加鎖,這會(huì)導(dǎo)致一定的性能開(kāi)銷(xiāo)

2 Operations:? 這個(gè)是基于OC實(shí)現(xiàn)的,以面向?qū)ο蟮姆绞椒庋b了需要執(zhí)行的操作,我們可以不必關(guān)心線程的管理和同步的問(wèn)題。它可以開(kāi)始,取消線程執(zhí)行。他有兩個(gè)默認(rèn)的實(shí)現(xiàn)方法:NSInvocationOperation和NSBlockOperation。當(dāng)然我們也可以自定NSOperations,只有實(shí)現(xiàn)里面的main方法即可

3? GCD:也是重點(diǎn)講的一個(gè),它是ios4才開(kāi)始使用的,當(dāng)然現(xiàn)在我們ios9都出來(lái)了,GCD的成熟度足可以讓我們放心的使用,它是基于C實(shí)現(xiàn)的,性能上要相對(duì)來(lái)說(shuō)好一些,而且可以用最簡(jiǎn)單的代碼去實(shí)現(xiàn)復(fù)雜的線程問(wèn)題。

三) NSThread的基本使用方法

1.動(dòng)態(tài)初始化

NSThread threadOne = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];

//其中run是一個(gè)方法,我們通過(guò)方法來(lái)執(zhí)行多線程中得處理

[_threadOne setName:@"one"]; //給線程初始化一個(gè)別名

[_threadOne start];//開(kāi)始執(zhí)行

2 靜態(tài)初始化

[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; //通過(guò)這一句代碼我們就可以創(chuàng)建出另一個(gè)線程出來(lái)并去執(zhí)行

3 創(chuàng)建隱式線程:

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

注:這是NSObject的一個(gè)方法,他可以讓一個(gè)耗時(shí)間的處理放入后臺(tái)去執(zhí)行,但是在swift中拋棄了,原因是蘋(píng)果覺(jué)得這個(gè)方法是線程不安全的

4 獲取當(dāng)前的線程

NSThread *current = [NSThread currentThread];//返回的是目前的線程

5 返回主線程(刷新UI控件必須在主線程中執(zhí)行)

[self performSelectorOnMainThread:@selector(main:) withObject:nil waitUntilDone:YES];

6 等待(暫停)當(dāng)前的線程

[NSThread sleepForTimeInterval:3.0f] ;//3秒之后執(zhí)行

或者

NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]];

[NSThread sleepUntilDate:date]; 等待date完成后再去執(zhí)行

注:前者是等待時(shí)間的完成,后者是等待數(shù)據(jù)的完成

四)? NSOperation使用方法:

NSOperation 實(shí)例封裝了需要執(zhí)行的操作和執(zhí)行操作所需的數(shù)據(jù),并且能夠以并發(fā)或非并發(fā)的方式執(zhí)行這個(gè)操作。NSOperation在ios4后也基于GCD實(shí)現(xiàn),但是相對(duì)于GCD來(lái)說(shuō)可控性更強(qiáng),并且可以加入操作依賴(lài)。NSOperation提供了ready cancelled executing finished這幾個(gè)狀態(tài)變化,我們的開(kāi)發(fā)也是必須處理自己關(guān)心的其中的狀態(tài)。這些狀態(tài)都是基于keypath的KVO通知決定,所以在你手動(dòng)改變自己關(guān)心的狀態(tài)時(shí),請(qǐng)別忘了手動(dòng)發(fā)送通知。這里面每個(gè)屬性都是相互獨(dú)立的,同時(shí)只可能有一個(gè)狀態(tài)是YES。finished這個(gè)狀態(tài)在操作完成后請(qǐng)及時(shí)設(shè)置為YES,因?yàn)镹SOperationQueue所管理的隊(duì)列中,只有isFinished為YES時(shí)才將其移除隊(duì)列,這點(diǎn)在內(nèi)存管理和避免死鎖很關(guān)鍵。

1.NSInvocationOperation:

NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];//紅色多線程中處理的方法

[op start]; //開(kāi)始

[op cancel];//取消

2? NSBlockOperation

NSBlockOperation *block = [NSBlockOperation? ? blockOperationWithBlock:^{

NSLog(@"%@",[NSThread currentThread]); //處理線程的block方法

}];

3 NSOperationQueue

一個(gè)NSOperation對(duì)象可以通過(guò)調(diào)用start方法來(lái)執(zhí)行任務(wù),默認(rèn)是同步執(zhí)行的。也可以將NSOperation添加到一個(gè)NSOperationQueue(操作隊(duì)列)中去執(zhí)行,而且是異步執(zhí)行的。一旦NSoperation添加到NSoperationQueue中,用戶(hù)就無(wú)權(quán)對(duì)NSoperation管理,都有NSoperationQueue來(lái)執(zhí)行。

3.1 添加一個(gè)隊(duì)列

NSoperationQueue *myQueue = [[NSOperationQueue alloc]init];

NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];

[myQueue addOperation:op];

3.2 主隊(duì)列(任何刷新UI的方法都必須在主隊(duì)列中執(zhí)行)

[[NSOperationQueue mainQueue]addOperationWithBlock:^{

//可以執(zhí)行刷新UI控件的方法

}];

3.3 隊(duì)列直接可以設(shè)置依賴(lài),比如隊(duì)列1需要隊(duì)列2執(zhí)行完畢后才能執(zhí)行(addDependency)

NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@",[NSThread currentThread]);

}];

NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@",[NSThread currentThread]);

}];

[block1 addDependency:block2];

[myQueue addOperation:block1];

[myQueue addOperation:block2];

3.4可以設(shè)置最大并發(fā)的操作數(shù)量

[myQueue setMaxConcurrentOperationCount:2];

3.5 一旦添加到隊(duì)列,隊(duì)列就擁有了這個(gè)Operation對(duì)象并且不能被? 刪除,唯一能做的事情是取消。

[myQueue cancelAllOperations];

3.6獲取NSOperation

myQueue.operations 這是一個(gè)數(shù)組,里面存放這添加進(jìn)入這個(gè)隊(duì)列的所有任務(wù)

3.7 如果你想臨時(shí)暫停Operations的執(zhí)行,可以使用queue的setSuspended:方法暫停queue。

[myQueue setSuspended:YES];

四)GCD

1 GCD是Grand Central Dispatch的簡(jiǎn)稱(chēng),它是基于C語(yǔ)言的。如果使用GCD,完全由系統(tǒng)管理線程,我們不需要編寫(xiě)線程代碼。只需定義想要執(zhí)行的任務(wù),然后添加到適當(dāng)?shù)恼{(diào)度隊(duì)列(dispatch queue)。GCD會(huì)負(fù)責(zé)創(chuàng)建線程和調(diào)度你的任務(wù),系統(tǒng)直接提供線程管理。

2 GCD的操作思想是講操作放在隊(duì)列中去執(zhí)行

1? 操作是用block來(lái)實(shí)現(xiàn)的

2 隊(duì)列是先進(jìn)先出的,它是負(fù)責(zé)調(diào)度任務(wù)執(zhí)行所在的線程

3 GCD分為串行和并行,有自定義,主隊(duì)列和全局隊(duì)列三種。一個(gè)同步函數(shù)只在完成了它預(yù)定的任務(wù)后才返回。一個(gè)異步函數(shù),剛好相反,會(huì)立即返回,預(yù)定的任務(wù)會(huì)完成但不會(huì)等它完成。因此,一個(gè)異步函數(shù)不會(huì)阻塞當(dāng)前線程去執(zhí)行下一個(gè)函數(shù)。如果在主隊(duì)列中執(zhí)行同步的話,會(huì)造成死鎖的發(fā)生

3 串行和并行

串行:一次只能執(zhí)行一個(gè)任務(wù), 當(dāng)前任務(wù)完成才開(kāi)始出列并啟動(dòng)下一個(gè)任務(wù)

并行:則盡可能多地啟動(dòng)任務(wù)并發(fā)執(zhí)行

4GCD基本使用方法

1 自定義隊(duì)列

dispatch_queue_t q = dispatch_queue_create("gcdDemo1", DISPATCH_QUEUE_SERIAL); DISPATCH_QUEUE_SERIAL:串行,可以傳入nil 默認(rèn)是串行,"gcdDemo1”是這個(gè)線程的別名,可以傳nil

dispatch_queue_t q = dispatch_queue_create("gcdDemo2", DISPATCH_QUEUE_CONCURRENT);? DISPATCH_QUEUE_CONCURRENT:并行

dispatch_async(q, ^{? ? 注:異步執(zhí)行,可以開(kāi)辟多個(gè)線程去執(zhí)行,無(wú)需等待

NSLog(@"%@",[NSThread currentThread]);

});

dispatch_sync(q, ^{? 注:同步執(zhí)行,只開(kāi)辟一個(gè)線程,需要等待上一個(gè)任務(wù)的完成才能執(zhí)行

NSLog(@"%@",[NSThread currentThread]);

});

2 全局隊(duì)列

dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

DISPATCH_QUEUE_PRIORITY_DEFAULT:默認(rèn)的,全局隊(duì)列是有優(yōu)先級(jí)的

DISPATCH_QUEUE_PRIORITY_HIGH? ? ? 最高(優(yōu)先執(zhí)行)

DISPATCH_QUEUE_PRIORITY_DEFAULT? ? 默認(rèn)

DISPATCH_QUEUE_PRIORITY_LOW? ? ? ? 最低

DISPATCH_QUEUE_PRIORITY_BACKGROUND 后臺(tái)

3 主隊(duì)列

dispatch_queue_t q = dispatch_get_main_queue();

每一個(gè)應(yīng)用程序都有一個(gè)主線程? 在ios中所有的ui刷新都再主線程中執(zhí)行!這是因?yàn)樘O(píng)果為了提高性能,大部分庫(kù)都是線程不安全的,如果在子線程刷新控件會(huì)造成一些問(wèn)題,所有所有的UI控件的刷新都由主線程上刷新

4 延遲執(zhí)行

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);

dispatch_after(time, dispatch_get_main_queue(), ^{

NSLog(@"我是3秒才執(zhí)行的!");

});

5 dispatchGroup組隊(duì)列

dispatchGroup作用:當(dāng) dispatch_group_async函數(shù)將多個(gè)任務(wù)關(guān)聯(lián)到一個(gè)Dispatch Group和相應(yīng)的queue中,group會(huì)并發(fā)地同時(shí)執(zhí)行這些任務(wù)。而且Dispatch Group可以用來(lái)阻塞一個(gè)線程, 直到group關(guān)聯(lián)的所有的任務(wù)完成執(zhí)行。有時(shí)候你必須等待任務(wù)完成的結(jié)果,然后才能繼續(xù)后面的處理。

dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, q, ^{

NSLog(@"1 == %@",[NSThread currentThread]);

});

dispatch_group_async(group, q, ^{

NSLog(@"2 == %@",[NSThread currentThread]);

});

dispatch_group_async(group, q, ^{

NSLog(@"3 == %@",[NSThread currentThread]);

});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{//不管前面的線程誰(shuí)先執(zhí)行,最后都會(huì)執(zhí)行notify方法。

NSLog(@"4 == %@",[NSThread currentThread]);

});

6 dispathc_apply

dispathc_apply是dispatch_sync 和dispatch_group的關(guān)聯(lián)API.它以指定的次數(shù)將指定的Block加入到指定的隊(duì)列中。并等待隊(duì)列中操作全部完成.

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(10, globalQueue, ^(size_t index) {

NSLog(@"%zu",index);

dispatch_source_merge_data(socurce, 1);

});

7 dispatch_source_t 信號(hào)源

dispatch source是一個(gè)監(jiān)視某些類(lèi)型事件的對(duì)象。當(dāng)這些事件發(fā)生時(shí),它自動(dòng)將一個(gè)block放入一個(gè)dispatch queue的執(zhí)行例程中。書(shū)中定義。我的理解就是多線程中得KVO,它檢測(cè)用戶(hù)事件,它是由dispatch_source_merge_data函數(shù)來(lái)向自己發(fā)送信號(hào),然后通過(guò)dispatch_source_set_event_handler這個(gè)函數(shù)去執(zhí)行。

這是我寫(xiě)了一個(gè)進(jìn)度條的例子

執(zhí)行部分:? __weak __typeof(self)weakSelf = self;

dispatch_source_t? socurce = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());

dispatch_source_set_event_handler(socurce, ^{

[weakSelf.progressIndicator setProgress:dispatch_source_get_data(socurce)? animated:YES];

});

dispatch_resume(socurce);

監(jiān)聽(tīng)部分:? ? dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(10, globalQueue, ^(size_t index) {

NSLog(@"%zu",index);

dispatch_source_merge_data(socurce, 1);

});

/*

dispatch_source_create(dispatch_source_type_t type,

uintptr_t handle,

unsigned long mask,

dispatch_queue_t queue);

第1個(gè)參數(shù):要監(jiān)聽(tīng)的事件類(lèi)型

第2個(gè)參數(shù):可以理解為句柄、索引或id,假如要監(jiān)聽(tīng)進(jìn)程,需要傳入進(jìn)程的ID

第3個(gè)參數(shù):根據(jù)參數(shù)2,可以理解為描述,提供更詳細(xì)的描述,讓它知道具體要監(jiān)聽(tīng)什么

第4個(gè)參數(shù):當(dāng)事件發(fā)生時(shí),將block添加至哪個(gè)隊(duì)列來(lái)執(zhí)行

**/

8 dispatch_semaphore信號(hào)量

當(dāng)我們?cè)谔幚硪幌盗芯€程的時(shí)候,當(dāng)數(shù)量達(dá)到一定量,在以前我們可能會(huì)選擇使用NSOperationQueue來(lái)處理并發(fā)控制,在GCD中我們需要通過(guò)dispatch_semaphore來(lái)控制它的并發(fā)數(shù)量。

dispatch_group_t group = dispatch_group_create();

dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

for (int i = 0; i < 10; i++)

{

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//如果技術(shù)器的數(shù)值大于等于1的時(shí)候進(jìn)行-1操作

dispatch_group_async(group, queue, ^{

NSLog(@"----%d",i);

sleep(2);

dispatch_semaphore_signal(semaphore);//計(jì)數(shù)器+1

});

}

簡(jiǎn)單的介紹一下這一段代碼,創(chuàng)建了一個(gè)初使值為5的semaphore,每一次for循環(huán)都會(huì)創(chuàng)建一個(gè)新的線程,線程結(jié)束的時(shí)候會(huì)發(fā)送一個(gè)信號(hào),線程創(chuàng)建之前會(huì)信號(hào)等待,所以當(dāng)同時(shí)創(chuàng)建了5個(gè)線程之后,for循環(huán)就會(huì)阻塞,等待有線程結(jié)束之后會(huì)增加一個(gè)信號(hào)才繼續(xù)執(zhí)行,如此就形成了對(duì)并發(fā)的控制,如上就是一個(gè)并發(fā)數(shù)為5的一個(gè)線程隊(duì)列。

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 一、前言 上一篇文章iOS多線程淺匯-原理篇中整理了一些有關(guān)多線程的基本概念。本篇博文介紹的是iOS中常用的幾個(gè)多...
    nuclear閱讀 2,144評(píng)論 6 18
  • NSThread 第一種:通過(guò)NSThread的對(duì)象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 952評(píng)論 0 3
  • 最近大家都在學(xué)習(xí)跟項(xiàng)目有關(guān)的知識(shí),有跟項(xiàng)目源碼學(xué)習(xí)有關(guān)的學(xué)習(xí)卡,也有跟項(xiàng)目有關(guān)的技術(shù)學(xué)習(xí)卡,比如:Jenkins,...
    Yvette14閱讀 763評(píng)論 3 1
  • Kiki_Shmoo閱讀 184評(píng)論 0 3
  • 不知怎么,這學(xué)期就是對(duì)很多事情都不感興趣,對(duì)籃球不感興趣,對(duì)學(xué)習(xí)也沒(méi)興趣,對(duì)什么事都覺(jué)得沒(méi)有意思。 狀態(tài)有些不...
    叁TROIS閱讀 215評(píng)論 0 1

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