NSThread使用&GCD使用&&NSOperation使用&&線程安全

純屬娛樂

0.0 簡介

(1) 進(jìn)程和線程基本概念【查看活動監(jiān)視器】

> 什么是進(jìn)程

a、是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序;

b、每個進(jìn)程之間是獨(dú)立的,每個進(jìn)程運(yùn)行在其專用且受保護(hù)的內(nèi)存空間;

> 什么是線程

a、一個進(jìn)程想要執(zhí)行任務(wù),必須得有線程(每個進(jìn)程至少要有一條線程);

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

> 線程的串行

a、一個線程中任務(wù)的執(zhí)行是串行的(在一個線程中執(zhí)行多個任務(wù),那么只能一個一個的按順序執(zhí)行,在同一時(shí)間內(nèi),一個線程只能執(zhí)行一個任務(wù));

> 線程的并行

a、一個進(jìn)程中可以開啟多條線程,每條線程可以并行(同時(shí))執(zhí)行不同的任務(wù);

【例如在一個線程中下載3個文件是串行;同時(shí)開啟3條線程分別下載3個文件是并行】

> 多線程的原理

a、單核CPU:同一時(shí)間,CPU只能處理一條線程,只有一條線程在工作(執(zhí)行);多線程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速的在多條線程之間調(diào)度(切換/輪詢);CPU調(diào)度線程的時(shí)間足夠快,看到的多線程并發(fā)(同時(shí))執(zhí)行的假象;

b、多核CPU

> 多線程優(yōu)缺點(diǎn)

a、優(yōu)點(diǎn):適當(dāng)提高程序的執(zhí)行效率;適當(dāng)提高資源利用率(CPU/內(nèi)存利用率);

b、缺點(diǎn):開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會占用大量的內(nèi)存空間,降低程序的性能;

> iOS創(chuàng)建線程的方式

pthread/NSThread/GCD/NSOperation

pthread用法

IPHONE4: 512m

安卓: 2G

安卓(多進(jìn)程): Linux + JAVA虛擬機(jī) + 應(yīng)用程序

(QQ100M + 微信100M + UC瀏覽器100M + 游戲500M + 系統(tǒng)1G)

蘋果(多任務(wù)): 512M只有一個人用

1,NSThread

a,顯示創(chuàng)建alloc init會創(chuàng)建新的線程,需要手動開啟start,線程可以設(shè)置名字name。方法運(yùn)行結(jié)束,線程結(jié)束。

2,b,隱式創(chuàng)建新線程,自動開啟

3,YES&&NO

1,YES即等待線程@selector操作做完才繼續(xù)執(zhí)行

2,NO即不等待線程線程操作完成

此圖請自動忽略水印

4,線程的生命周期:

? ? ? ? ? ? ?1,alloc+init:即創(chuàng)建對象,在運(yùn)行的內(nèi)存中;

? ? ? ? ? ? 2,start:即線程為就緒態(tài) (萬事具備,等待CPU);

? ? ? ? ? ? 3,當(dāng)CPU調(diào)度當(dāng)前線程:即線程為運(yùn)行態(tài);

? ? ? ? ? ? 4當(dāng)CPU調(diào)度其他線程:即線程又回到就緒態(tài);

? ? ? ? ? ? 5,當(dāng)線程調(diào)用了sleep、等待同步鎖:即線程為阻塞態(tài);

? ? ? ? ? ? 6,當(dāng)線程中sleep完、得到同步鎖:即線程變?yōu)榫途w態(tài);

? ? ? ? ? ? ?7,當(dāng)線程任務(wù)執(zhí)行完成/異常/強(qiáng)制退出:即線程為死亡態(tài);

5,線程安全 沒有加鎖,訪問的數(shù)據(jù)不是想要的

6,線程安全 沒有加鎖,訪問的數(shù)據(jù)不是想要的

7,線程安全 使用@synchronized加鎖,訪問的數(shù)據(jù)是想要的

8,線程安全 使用NSLock加鎖,訪問的數(shù)據(jù)是想要的

9,GCD簡介

> 什么是GCD

a、全稱是Grand Center Dispatch,即中樞調(diào)度器;

b、純C語言,提供了非常多強(qiáng)大的函數(shù);

> GCD的優(yōu)勢

a、GCD是蘋果公司為多核的并行運(yùn)算提出的解決方案;

b、GCD會自動利用更多的CPU內(nèi)核(比如雙核、四核);

c、GCD會自動管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程);

d、程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼;

> GCD的2個核心概念

a、任務(wù):執(zhí)行什么操作(執(zhí)行下載、播放音樂等);

b、隊(duì)列:用來存放任務(wù);

> GCD的使用步驟

a、定制任務(wù)[確定想要做的事情];

b、將任務(wù)添加到隊(duì)列中[GCD會自動將隊(duì)列中的任務(wù)取出,放到對應(yīng)線程中執(zhí)行;另外任務(wù)的取出是遵循隊(duì)列的FIFO原則:先進(jìn)先出];

> GCD基本使用

a、GCD中的2個用來執(zhí)行任務(wù)的函數(shù)

// 同步方式執(zhí)行任務(wù)[queue隊(duì)列,block任務(wù)]

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

// 異步方式執(zhí)行任務(wù)[queue隊(duì)列,block任務(wù)]

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

b、GCD同步和異步的區(qū)別

- 同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力;

- 異步:可以在新的線程中執(zhí)行任務(wù),具備可啟新線程的能力;

[具備開啟線程的能力,但不代表一定會開啟線程?。?!]

c、GCD隊(duì)列的兩大類型

- 并發(fā)隊(duì)列

多個任務(wù)并發(fā)(同時(shí))執(zhí)行(自動開啟多個線程同時(shí)執(zhí)行任務(wù));

并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效;

- 串行隊(duì)列

多個任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù));

d、容易混淆的術(shù)語[同步、異步、并發(fā)、串行]

- 同步和異步主要影響:具不具備開啟新的線程 (并不代表一定會開線程!);

- 并行和串行主要影響:任務(wù)的執(zhí)行方式;

e、并行隊(duì)列

- GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列,供整個應(yīng)用使用,不需要手動創(chuàng)建

dispatch_get_global_queue函數(shù)獲得全局的并發(fā)隊(duì)列

dispatch_queue_t dispatch_get_global_queue(long identifier, // 隊(duì)列的優(yōu)先級

unsigned long flags); // 參數(shù)保留,用0即可

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 獲得全局并發(fā)隊(duì)列

- 全局并發(fā)隊(duì)列的優(yōu)先級

DISPATCH_QUEUE_PRIORITY_HIGH? ? ? 高

DISPATCH_QUEUE_PRIORITY_DEFAULT? ? 默認(rèn)(中)

DISPATCH_QUEUE_PRIORITY_LOW? ? ? ? 低

DISPATCH_QUEUE_PRIORITY_BACKGROUND 后臺

f、串行隊(duì)列

- 使用dispatch_queue_create函數(shù)創(chuàng)建串行隊(duì)列

- 使用dispatch_get_main_queue()獲得主隊(duì)列(跟主線程相關(guān)的隊(duì)列,用于線程間通信)

[主隊(duì)列是GCD自帶的一種特殊串行隊(duì)列;放在主隊(duì)列中的任務(wù),都會放到主線程中執(zhí)行]

GCD、延時(shí)執(zhí)行

/** 方式1,這種延時(shí)操作是不可取的,因?yàn)檠訒r(shí)操作是在主線程,即會卡住主線程,如果sleep在其他線程則也會卡住對應(yīng)線程*/

? 方式1 ? 延時(shí)3秒[NSThread sleepForTimeInterval:3];

? 方式2 ? 定制好任務(wù)后,不會卡住當(dāng)前線程

[self performSelector:@selector(downloadImage) withObject:nil afterDelay:3];

?方式3 ?dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"圖片下載");

});

> GCD中線程間的通信

communication

> GCD一次性代碼只執(zhí)行一次

寫單例

10,總結(jié)

11,異步+并發(fā) 開啟新線程 多個任務(wù)同時(shí)執(zhí)行

12,異步+串行 開啟新線程 任務(wù)依次同時(shí)執(zhí)行

13,同步+并發(fā) 不開啟新線程 任務(wù)依次執(zhí)行

14,同步+串行 不開新線程 任務(wù)依次執(zhí)行

15,異步+主隊(duì)列 不開啟新線程 任務(wù)依次執(zhí)行(主隊(duì)列任務(wù)是一次執(zhí)行的,并且是異步)

16,同步+主隊(duì)列 不開啟新線程 任務(wù)依次執(zhí)行(堅(jiān)決反對使用這個,容易卡)

17,pthread

18,NSOperation(基于GCD封裝)

> NSOperation和NSOperationQueue實(shí)現(xiàn)多線程的具體步驟:

a、將需要執(zhí)行的操作封裝到一個NSOperation對象中;

b、然后將NSOperation對象添加到NSOperationQueue中;

c、系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來;

d、將取出來的NSOperation封裝的操作放到一條新線程中執(zhí)行;

【系統(tǒng)會自動處理,不用管多少條線程】

> NSOperation的基本使用

a、NSOperation是抽象類,不具備封裝操作的能力,必須使用它的子類;

b、使用NSOperation子類的方式有3種

- 1,NSInvocationOperation

- 2,NSBlockOperation

- 3,自定義子類繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法

> NSInvocationOperation

?創(chuàng)建操作

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

> NSBlockOperation

沒有添加到隊(duì)列中,直接調(diào)用operation的start方法

當(dāng)任務(wù)個數(shù)為一的時(shí)候,就是同步執(zhí)行;

當(dāng)任務(wù)個數(shù)大于一的時(shí)候,就會異步執(zhí)行;

手動開啟 (沒有添加到隊(duì)列中,就需要手動開啟)

[operation start];

添加到隊(duì)列中 [自動異步執(zhí)行]

[queue addOperation:operation];

> NSOperationQueue

1、創(chuàng)建一個隊(duì)列(非主隊(duì)列)

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

2、任務(wù)任務(wù)

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"operation1? ? ? 下載圖片---%@",[NSThread currentThread]);

}];

[operation1 addExecutionBlock:^{

NSLog(@"operation2? ? ? 下載圖片---%@",[NSThread currentThread]);

}];

3、添加到隊(duì)列中(多個任務(wù)會自動異步執(zhí)行任務(wù),并發(fā))

[queue addOperation:operation1];

// 更為簡單的寫法? 自動異步執(zhí)行任務(wù),并發(fā)

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue addOperationWithBlock:^{

NSLog(@"operation3? ? ? 下載圖片---%@",[NSThread currentThread]);

}];

設(shè)置最大并發(fā)數(shù)[這可以更好的保證程序的性能],即控制并發(fā)執(zhí)行最大線程數(shù)量,以節(jié)省內(nèi)存空間

queue.maxConcurrentOperationCount = 2;

a、NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序(注意不能相互依賴);

添加依賴 (和添加到隊(duì)列的先后順序無關(guān))

[operationC addDependency:operationB];

[operationB addDependency:operationA];

b、可以在不同queue中的NSOperation之間創(chuàng)建依賴;

> 線程間通信 回到主線程設(shè)置顯示


> 隊(duì)列的取消、暫停、恢復(fù)

a、取消隊(duì)列的所有操作

- (void)cancelAllOperations; 【在內(nèi)存警告的時(shí)候可以添加上該方法】

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

b、暫停和恢復(fù)隊(duì)列

- (void)SetSuspended:(BOOL)b; 【YES表示暫停隊(duì)列,NO表示恢復(fù)隊(duì)列】

[滾動視圖(注意表格視圖也是繼承自UIScrollView的)性能優(yōu)化中可以使用到,開始拖動的時(shí)候暫停隊(duì)列下載,拖動結(jié)束后恢復(fù)隊(duì)列]

接收到內(nèi)存警告

- (void)didReceiveMemoryWarning{

[super didReceiveMemoryWarning];

// 取消隊(duì)列操作

}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{

//開始拖動,暫停隊(duì)列操作

}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{

// 結(jié)束拖動,恢復(fù)隊(duì)列操作

}

(5) 線程安全(在GCD以后,已經(jīng)幫我們處理了這些問題)

?線程安全問題

實(shí)例:3個窗口賣票

> 3個窗口同時(shí)賣票,即多條線程訪問同一資源;

> 線程哪個先調(diào)用是不確定的,先調(diào)度誰是CPU的事;

> 當(dāng)線程任務(wù)執(zhí)行完成,線程為死亡態(tài)(如果是沒有while循環(huán),但第二次點(diǎn)擊的時(shí)候程序就崩潰);

> 多線程的安全隱患

a、資源共享(資源搶占):一塊資源可能會被多個線程共享(即多個線程可能訪問一個資源);

(當(dāng)多個線程訪問同一塊資源時(shí),很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全的問題)[例如一個線程取錢,一個線程存錢;例如賣票,多個窗口賣票即多條線程賣]

> 互斥鎖的使用

a、互斥鎖方式一:@synchronized(鎖對象){//需要加鎖的代碼}

// 鎖定一份代碼只用一把鎖,用多把鎖是無效的;

b、互斥鎖方式二:

/** 1、互斥鎖的初始化*/

_myLock = [[NSLock alloc] init];

// 2、加鎖方式2

[_myLock lock];

// 需要加鎖的代碼

// 3、解鎖

[_myLock unlock];

> 互斥鎖的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題;

缺點(diǎn):需要消耗大量CPU資源;

> 互斥鎖使用的前提:多條線程搶占同一資源;

> 單例的線程安全

> 數(shù)據(jù)庫操作的線程安全

nonatomic(atomic):指定set、get方法是否原子操作。原子操作,主要指是否線程安全。如果使用atomic那么set、get方法都是線程安全的--- 當(dāng)一個線程進(jìn)入get、set方法之后,其他線程無法進(jìn)入該set、get方法,那么久避免多線程并發(fā)破壞數(shù)據(jù)完整性,atomic是默認(rèn)值。雖然atomic可以保證對象數(shù)據(jù)的完整性,但atomic線程安全會造成性能下降(因?yàn)闀?dǎo)致其他線程的阻塞)。因此,大多數(shù)單線程環(huán)境下,都是使用nonatomic來提高set、get方法的訪問性能;

線程注意點(diǎn):

1、不要同時(shí)開太多線程(1~3條線程即可,不要超過5條);

2、線程概念:

主線程:UI線程,顯示、刷新UI界面,處理UI控件事件;

子線程:后臺線程,異步線程;

3、不要把耗時(shí)操作放在主線程,要放在子線程中執(zhí)行;

19.GCD使用

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

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

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