ios 多線程,線程生命為周期,多線程的使用,NSThread,GCD,NSOperation

前言:

最近想回顧一下多線程問題,看到一篇文章寫的非常詳細(xì),為了便于以后查找以及加深印象,就照著原文摘錄了下文,原著勿怪:

原文地址:http://www.cocoachina.com/ios/20170707/19769.html

一,多線程的基本概念

進(jìn)程:? 可以理解成一個運行中的應(yīng)用程序,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ),主要管理資源.

線程:? 是進(jìn)程的基本執(zhí)行單元,一個進(jìn)程對應(yīng)多個線程.

主線程:? 處理UI,所有更新UI的操作都必須在主線程中執(zhí)行,不要把耗時操作放在主線程,會卡界面.

多線程:? 在同一時刻,一個CPU只能處理1條線程,但是CPU可以在多條線程之間快速切換,只要切換的足夠快,就造成了多線程一同執(zhí)行的假象.

多線程是通過提高資源使用率來提高總體的效率.

我們運用多線程的目的是:將耗時操作放在后臺運行.

二,線程狀態(tài)與生命周期

下圖是線程狀態(tài)示意圖,從圖中可以看出線程的生命周期是:? 新建 -就緒 -運行 - 阻塞 - 死亡

下面是生命周期的每一步:

新建:?? 實例化線程對象.

就緒:? 向線程對象發(fā)送start消息,線程對象被加入線程池等待CPU調(diào)度.

運行:? CPU負(fù)責(zé)調(diào)度可調(diào)度線程池中線程的執(zhí)行.線程執(zhí)行完成前,狀態(tài)可能會在就緒和運行之間來回切換.就緒和運行之間的狀態(tài)變化由CPU負(fù)責(zé),程序不能干預(yù).

阻塞:? 當(dāng)滿足某個預(yù)定條件時,可以使用休眠或鎖,阻塞線程執(zhí)行,sleepForTimeInterval(休眠指定時間), sleepUntilDate(休眠到指定日期),@synchronized(self):(互斥鎖).

還有線程的exit和cancel.

[NSThread exit]:一旦強行終止線程,后續(xù)的所有代碼都不會被執(zhí)行.

[thread cancel]取消:? 并不會直接取消線程,只是給線程添加isCancelled標(biāo)記.

三,多線程的四種解決方案

多線程的四種解決方案分別是:pthread,NSThread, GCD,NSOperation.

下面是對這四種方案進(jìn)行了解讀和對比.


四,線程安全問題

當(dāng)多個線程訪問同一塊資源時,很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全問題.就好比幾個人在同一時修改一個表格,造成數(shù)據(jù)的錯亂.

解決多線程安全問題的方法

方法一:? 互斥鎖(同步鎖)

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

判斷的時候鎖對象要存在,如果代碼中只有一個地方需要加鎖,大多時候使用self作為鎖對象,這樣可以避免單獨再創(chuàng)建一個鎖對象.

加了互斥鎖的代碼,當(dāng)新線程訪問時,如果發(fā)現(xiàn)其他線程正在執(zhí)行鎖定的代碼,新線程就會進(jìn)入休眠

方法二:? 自旋鎖

加了自旋鎖,當(dāng)新線程訪問代碼時,如果發(fā)現(xiàn)其他線程正在鎖定代碼,新線程會用死循環(huán)的方式,一直等待鎖定的代碼執(zhí)行完成.相當(dāng)于不停嘗試執(zhí)行代碼,比較小號性能.

屬性修飾atomic本身就是一把自旋鎖.

下面說一下屬性修飾nonatomic 和atomic

nonatomic 非原子性的,同一時間可以有很多線程讀和寫.

atomic原子屬性(線程安全), 保證同一時間只有一個縣城能夠?qū)懭?但是同一個時間多個線程都可以取值),atomic本身就有一把鎖(自旋鎖).

atomic: 線程安全,需消耗大量的資源

nonatomic: 非線程安全,不過效率更高,一般用nonatomic

五,NSThread的使用

No.1:NTHread創(chuàng)建線程

NSThread有三種創(chuàng)建方式:

init方式

detachNewThreadSelector創(chuàng)建好之后自動自動

pefromSelectorInBackground創(chuàng)建好之后也是直接啟動

/** 方法一, 需要Start */

? ? NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething1:) object:@"NSThread1"];

? ? //線程加入線程池等待CPU調(diào)度,時間很快,幾乎是立刻執(zhí)行

? ? [thread1 start];

/** 方法二, 創(chuàng)建好之后自動啟動 */

? ? [NSThread detachNewThreadSelector:@selector(doSomething2:) toTarget:self withObject:@"NSThread2"];

/** 隱式創(chuàng)建, 直接啟動 */

? ? [self performSelectorInBackground:@selector(doSomething3:) withObject:@"NSThread3"];

No.2:NSThread的類方法

返回當(dāng)前線程

//?當(dāng)前線程

[NSThread?currentThread];

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


//?如果number=1,則表示在主線程哪亿,否則是子線程

打印結(jié)果:{number?=?1,?name?=?main}

阻塞休眠

//休眠多久

[NSThread?sleepForTimeInterval:2];

//休眠到指定時間

[NSThread?sleepUntilDate:[NSDate?date]];

類方法補充

//退出線程

[NSThread?exit];

//判斷當(dāng)前線程是否為主線程

[NSThread?isMainThread];

//判斷當(dāng)前線程是否是多線程

[NSThread?isMultiThreaded];

//主線程的對象

NSThread?*mainThread?=?[NSThread?mainThread];

No.3:NSThread的一些屬性

//線程是否在執(zhí)行

thread.isExecuting;

//線程是否被取消

thread.isCancelled;

//線程是否完成

thread.isFinished;

//是否是主線程

thread.isMainThread;

//線程的優(yōu)先級约计,取值范圍0.0到1.0,默認(rèn)優(yōu)先級0.5淳衙,1.0表示最高優(yōu)先級攻走,優(yōu)先級高风纠,CPU調(diào)度的頻率高

?thread.threadPriority;

六,GCD的理解與使用

No.1:GCD的特點

GCD會自動利用更多的CPU內(nèi)核;

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

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

No.2: GCD的基本概念

任務(wù) (block) : 任務(wù)就是將要在線程中執(zhí)行的代碼,將這段代碼用block封裝好,然后將這個任務(wù)添加到指定的執(zhí)行方式(同步執(zhí)行,異步執(zhí)行), 等待CPU從隊列中取出任務(wù)放到對應(yīng)的線程中執(zhí)行.

同步 (sync):一個接著一個,前一個沒有執(zhí)行完, 后面的不能執(zhí)行,不開線程.

異步 (async) : 開啟多線程, 任務(wù)同一時間可以一起執(zhí)行. 異步是多線程的代名詞.

隊列 :? 裝載線程任務(wù)的隊形結(jié). (系統(tǒng)以先進(jìn)先出的方式調(diào)度隊列中的任務(wù)執(zhí)行). 在GCD中有兩種隊列: 串行隊列和并發(fā)隊列.

串行隊列 :線程只能依次有序的執(zhí)行.

并發(fā)隊列 :線程可以同時一起執(zhí)行.實際上是CPU在多條線程之間快速切換.(并發(fā)只有在異步(dispatch_async)函數(shù)下才有效).

GCD總結(jié) : 將任務(wù)(要在線程中執(zhí)行的操作block) 添加到隊列(自己創(chuàng)建或使用全局并發(fā)隊列),并且指定任務(wù)的執(zhí)行方式(異步dispatch_async, 同步dispatch_sync).

No.3 : 隊列的創(chuàng)建方法

使用dispatch_queue_create來創(chuàng)建隊列對象,傳入兩個參數(shù),第一個表示隊列的唯一標(biāo)示符,可為空.第二個參數(shù)用來標(biāo)示串行隊列(DISPATCH_QUEUE_SERIAL) 或并發(fā)隊列 (DISPATCH_QUEUE_CONCURRENT).

//串行隊列
??? dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
??? //并發(fā)隊列
??? dispatch_queue_t queue1 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

GCD隊列還有另外兩種:

主隊列,全局并發(fā)隊列

主隊列 :主隊列是負(fù)責(zé)在主線程上調(diào)度任務(wù), 如果在主線程上已經(jīng)有任務(wù)正在執(zhí)行,主隊列會等到主線程空閑后再調(diào)度任務(wù), 通常是返回主線程更新UI的時候使用. dispatch_get_main_queue()

dispatch_async(dispatch_get_global_queue(0, 0), ^{

? ? ? ? //耗時操作放在這里


? ? ? ? dispatch_async(dispatch_get_main_queue(), ^{

? ? ? ? ? ? //回到主線程進(jìn)行UI操作


? ? ? ? });

? ? });

全局并發(fā)隊列: 全局并發(fā)隊列就是一個并發(fā)隊列,是為了讓我們更方便的使用多線程.

dispatch_get_global_queue(0,0).

//全局并發(fā)隊列

dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);

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

#define?DISPATCH_QUEUE_PRIORITY_HIGH?2?//?高優(yōu)先級

#define?DISPATCH_QUEUE_PRIORITY_DEFAULT?0?//?默認(rèn)(中)優(yōu)先級

#define?DISPATCH_QUEUE_PRIORITY_LOW?(-2)?//?低優(yōu)先級

#define?DISPATCH_QUEUE_PRIORITY_BACKGROUND?INT16_MIN?//?后臺優(yōu)先級

//iOS8開始使用服務(wù)質(zhì)量,現(xiàn)在獲取全局并發(fā)隊列時,可以直接傳0

dispatch_get_global_queue(0,?0);

No.4:同步/異步/任務(wù)饵较、創(chuàng)建方式

同步 (sync) 使用dispatch_sync來表示

異步 (sync) 使用dispatch_async來表示

任務(wù)就是將要在線程中執(zhí)行的代碼, 將這段代碼用block封裝好.

代碼如下:

//同步執(zhí)行任務(wù)

? ? dispatch_sync(dispatch_get_global_queue(0, 0), ^{

? ? ? ? //任務(wù)放在這個block里

? ? ? ? NSLog(@"我是同步執(zhí)行任務(wù)");

? ? });

? ? //異步執(zhí)行任務(wù)

? ? dispatch_async(dispatch_get_global_queue(0, 0), ^{

? ? ? ? //

? ? ? ? //任務(wù)放在這個block里

? ? ? ? NSLog(@"我是異步執(zhí)行任務(wù)");

? ? });

No.5: GCD的使用

由于有多種隊列(串行/并發(fā)/主隊列) 和兩種執(zhí)行方式 (同步/異步),所以他們之間可以有多種組合方式.

1, 串行同步

2,串行異步

3,并發(fā)同步

4,并發(fā)異步

5,主隊列同步

6,主隊列異步

◎串行同步

執(zhí)行完一個任務(wù),在執(zhí)行下一個任務(wù),不開啟新線程.

/**?串行同步?*/

-?(void)syncSerial?{


????NSLog(@"\n\n**************串行同步***************\n\n");


????//?串行隊列

????dispatch_queue_t?queue?=?dispatch_queue_create("test",?DISPATCH_QUEUE_SERIAL);


????//?同步執(zhí)行

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"串行同步1???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"串行同步2???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"串行同步3???%@",[NSThread?currentThread]);

????????}

????});

}

輸出結(jié)果為順序執(zhí)行,都在主線程:

串行同步1???{number?=?1,?name?=?main}

串行同步1???{number?=?1,?name?=?main}

串行同步1???{number?=?1,?name?=?main}

串行同步2???{number?=?1,?name?=?main}

串行同步2???{number?=?1,?name?=?main}

串行同步2???{number?=?1,?name?=?main}

串行同步3???{number?=?1,?name?=?main}

串行同步3???{number?=?1,?name?=?main}

串行同步3???{number?=?1,?name?=?main}

◎串行異步

開啟新線程,但因為任務(wù)是串行的,所以還是按順序執(zhí)行完任務(wù).

/**?串行異步?*/

-?(void)asyncSerial?{


????NSLog(@"\n\n**************串行異步***************\n\n");


????//?串行隊列

????dispatch_queue_t?queue?=?dispatch_queue_create("test",?DISPATCH_QUEUE_SERIAL);


????// 異步執(zhí)行

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"串行異步1???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"串行異步2???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"串行異步3???%@",[NSThread?currentThread]);

????????}

????});

}

輸出結(jié)果為順序執(zhí)行,有不同線程:

串行異步1???{number?=?3,?name?=?(null)}

串行異步1???{number?=?3,?name?=?(null)}

串行異步1???{number?=?3,?name?=?(null)}

串行異步2???{number?=?3,?name?=?(null)}

串行異步2???{number?=?3,?name?=?(null)}

串行異步2???{number?=?3,?name?=?(null)}

串行異步3???{number?=?3,?name?=?(null)}

串行異步3???{number?=?3,?name?=?(null)}

串行異步3???{number?=?3,?name?=?(null)}

◎并發(fā)同步

因為是同步的,所以執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù).不會開啟新線程

/**?并發(fā)同步?*/

-?(void)syncConcurrent?{

????NSLog(@"\n\n**************并發(fā)同步***************\n\n");

????//?并發(fā)隊列

????dispatch_queue_t?queue?=?dispatch_queue_create("test",?DISPATCH_QUEUE_CONCURRENT);

????//?同步執(zhí)行

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"并發(fā)同步1???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"并發(fā)同步2???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"并發(fā)同步3???%@",[NSThread?currentThread]);

????????}

????});

}

輸出結(jié)果為順序執(zhí)行,都在主線程:

并發(fā)同步1???{number?=?1,?name?=?main}

并發(fā)同步1???{number?=?1,?name?=?main}

并發(fā)同步1???{number?=?1,?name?=?main}

并發(fā)同步2???{number?=?1,?name?=?main}

并發(fā)同步2???{number?=?1,?name?=?main}

并發(fā)同步2???{number?=?1,?name?=?main}

并發(fā)同步3???{number?=?1,?name?=?main}

并發(fā)同步3???{number?=?1,?name?=?main}

并發(fā)同步3???{number?=?1,?name?=?main}

◎并發(fā)異步

任務(wù)交替執(zhí)行,開啟多線程.

/**?并發(fā)異步?*/

-?(void)asyncConcurrent?{


????NSLog(@"\n\n**************并發(fā)異步***************\n\n");


????//?并發(fā)隊列

????dispatch_queue_t?queue?=?dispatch_queue_create("test",?DISPATCH_QUEUE_CONCURRENT);


????//?同步執(zhí)行

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"并發(fā)異步1???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"并發(fā)異步2???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"并發(fā)異步3???%@",[NSThread?currentThread]);

????????}

????});

}

輸出結(jié)果為無序執(zhí)行,有多條線程:

并發(fā)異步1???{number?=?3,?name?=?(null)}

并發(fā)異步2???{number?=?4,?name?=?(null)}

并發(fā)異步3???{number?=?5,?name?=?(null)}

并發(fā)異步1???{number?=?3,?name?=?(null)}

并發(fā)異步2???{number?=?4,?name?=?(null)}

并發(fā)異步3???{number?=?5,?name?=?(null)}

并發(fā)異步1???{number?=?3,?name?=?(null)}

并發(fā)異步2???{number?=?4,?name?=?(null)}

并發(fā)異步3???{number?=?5,?name?=?(null)}

◎ 主隊列同步

如果在主線程中運用這種方式,則會發(fā)生死鎖,程序崩潰.

/**?主隊列同步?*/

-?(void)syncMain?{


????NSLog(@"\n\n**************主隊列同步拍嵌,放到主線程會死鎖***************\n\n");


????//?主隊列

????dispatch_queue_t?queue?=?dispatch_get_main_queue();


????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"主隊列同步1???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"主隊列同步2???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"主隊列同步3???%@",[NSThread?currentThread]);

????????}

????});

}

主隊列同步造成死鎖的原因

1,如果在主線程中運用主隊列同步,也就是把任務(wù)放到主線程的隊列中.

2,而同步對于任務(wù)是立刻執(zhí)行的,那么當(dāng)把第一個任務(wù)放進(jìn)主隊列時,他就立馬執(zhí)行

3,可是主線程現(xiàn)在正在處理syncMain方法,任務(wù)需要等sybcMain執(zhí)行完才能執(zhí)行.

4,sysnMain執(zhí)行到第一個任務(wù)的時候,又要等第一個任務(wù)執(zhí)行完才能往下執(zhí)行第二個和第三個任務(wù).

5,這樣syncMain方法和第一個任務(wù)就開始了互相等待,形成了死鎖.

◎主隊列異步

在主線程中任務(wù)按順序執(zhí)行

/**?主隊列異步?*/

-?(void)asyncMain?{


????NSLog(@"\n\n**************主隊列異步***************\n\n");


????//?主隊列

????dispatch_queue_t?queue?=?dispatch_get_main_queue();


????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"主隊列異步1???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"主隊列異步2???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_sync(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"主隊列異步3???%@",[NSThread?currentThread]);

????????}

????});

}

輸出結(jié)果為在主線程中按順序執(zhí)行:

主隊列異步1???{number?=?1,?name?=?main}

主隊列異步1???{number?=?1,?name?=?main}

主隊列異步1???{number?=?1,?name?=?main}

主隊列異步2???{number?=?1,?name?=?main}

主隊列異步2???{number?=?1,?name?=?main}

主隊列異步2???{number?=?1,?name?=?main}

主隊列異步3???{number?=?1,?name?=?main}

主隊列異步3???{number?=?1,?name?=?main}

主隊列異步3???{number?=?1,?name?=?main}

◎GCD之間的通訊

開發(fā)中需要在主線程上進(jìn)行UI的相關(guān)操作,通常會把一些耗時的操作放在其他線程,比如說圖片文件下載等耗時操作.當(dāng)完成了耗時操作之后,需要回到主線程進(jìn)行UI的處理,這里用到了線程之間的通訊.

-?(IBAction)communicationBetweenThread:(id)sender?{


????//?異步

????dispatch_async(dispatch_get_global_queue(0,?0),?^{

????????//?耗時操作放在這里,例如下載圖片循诉。(運用線程休眠兩秒來模擬耗時操作)

????????[NSThread?sleepForTimeInterval:2];

????????NSString?*picURLStr?=?@"http://www.bangmangxuan.net/uploads/allimg/160320/74-160320130500.jpg";

????????NSURL?*picURL?=?[NSURL?URLWithString:picURLStr];

????????NSData?*picData?=?[NSData?dataWithContentsOfURL:picURL];

????????UIImage?*image?=?[UIImage?imageWithData:picData];


????????//?回到主線程處理UI

????????dispatch_async(dispatch_get_main_queue(),?^{

????????????//?在主線程上添加圖片

????????????self.imageView.image?=?image;

????????});

????});

}

上面的代碼實在新開的線程中進(jìn)行圖片下載,下載完成之后回到主線程顯示圖片.

◎GCD柵欄

當(dāng)任務(wù)需要異步進(jìn)行,但是這些任務(wù)需要分成兩組來完成,第一組完成之后才能進(jìn)行第二組, 這時候就用到了GCD的柵欄方法

dispatch_barrier_async.

-?(IBAction)barrierGCD:(id)sender?{


????//?并發(fā)隊列

????dispatch_queue_t?queue?=?dispatch_queue_create("test",?DISPATCH_QUEUE_CONCURRENT);


????//?異步執(zhí)行

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"柵欄:并發(fā)異步1???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"柵欄:并發(fā)異步2???%@",[NSThread?currentThread]);

????????}

????});


????dispatch_barrier_async(queue,?^{

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

????????NSLog(@"*******?并發(fā)異步執(zhí)行横辆,但是34一定在12后面?*********");

????});


????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"柵欄:并發(fā)異步3???%@",[NSThread?currentThread]);

????????}

????});

????dispatch_async(queue,?^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"柵欄:并發(fā)異步4???%@",[NSThread?currentThread]);

????????}

????});

}

上面的代碼打印結(jié)果如下,開了多條線程,所有任務(wù)都是并發(fā)異步進(jìn)行.但是第一組完成之后,才會進(jìn)行第二組的操作.

柵欄:并發(fā)異步1???{number?=?3,?name?=?(null)}

柵欄:并發(fā)異步2???{number?=?6,?name?=?(null)}

柵欄:并發(fā)異步1???{number?=?3,?name?=?(null)}

柵欄:并發(fā)異步2???{number?=?6,?name?=?(null)}

柵欄:并發(fā)異步1???{number?=?3,?name?=?(null)}

柵欄:并發(fā)異步2???{number?=?6,?name?=?(null)}

?------------barrier------------{number?=?6,?name?=?(null)}

*******?并發(fā)異步執(zhí)行,但是34一定在12后面?*********

柵欄:并發(fā)異步4???{number?=?3,?name?=?(null)}

柵欄:并發(fā)異步3???{number?=?6,?name?=?(null)}

柵欄:并發(fā)異步4???{number?=?3,?name?=?(null)}

柵欄:并發(fā)異步3???{number?=?6,?name?=?(null)}

柵欄:并發(fā)異步4???{number?=?3,?name?=?(null)}

柵欄:并發(fā)異步3???{number?=?6,?name?=?(null)}

◎GCD延時執(zhí)行

當(dāng)需要等待一會再執(zhí)行,就可以用到這個方法了:dispatch_after

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,?(int64_t)(5.0?*?NSEC_PER_SEC)),?dispatch_get_main_queue(),?^{

????//?5秒后異步執(zhí)行

????NSLog(@"我已經(jīng)等待了5秒打洼!");

});

GCD實現(xiàn)代碼只執(zhí)行一次

使用dispatch_once能保證某段代碼在程序運行過程中只被執(zhí)行1次龄糊。可以用來設(shè)計單例募疮。

static?dispatch_once_t?onceToken;

dispatch_once(&onceToken,?^{

????NSLog(@"程序運行過程中我只執(zhí)行了一次炫惩!");

});

◎GCD快速迭代

GCD有一個快速迭代的方法dispatch_apply, dispatch_apply可以同時遍歷多個數(shù)字

-?(IBAction)applyGCD:(id)sender?{


????NSLog(@"\n\n**************?GCD快速迭代?***************\n\n");


????//?并發(fā)隊列

????dispatch_queue_t?queue?=?dispatch_get_global_queue(0,?0);


????//?dispatch_apply幾乎同時遍歷多個數(shù)字

????dispatch_apply(7,?queue,?^(size_t?index)?{

????????NSLog(@"dispatch_apply:%zd======%@",index,?[NSThread?currentThread]);

????});

}

打印結(jié)果如下:

dispatch_apply:0======{number?=?1,?name?=?main}

dispatch_apply:1======{number?=?1,?name?=?main}

dispatch_apply:2======{number?=?1,?name?=?main}

dispatch_apply:3======{number?=?1,?name?=?main}

dispatch_apply:4======{number?=?1,?name?=?main}

dispatch_apply:5======{number?=?1,?name?=?main}

dispatch_apply:6======{number?=?1,?name?=?main}

◎GCD隊列組

異步執(zhí)行幾個耗時操作,當(dāng)這幾個操作都完成之后再執(zhí)行另一個操作,就可以用到隊列組了.

隊列組有下面幾個特點:

1,所有的任務(wù)會并發(fā)的執(zhí)行(不按順序).

2,所有的異步函數(shù)都添加到隊列中,然后再納入隊列組的監(jiān)聽范圍.

3,使用dispatch_group_notify函數(shù),來監(jiān)聽上面的任務(wù)是否完成,如果完成,就調(diào)用這個方法.

隊列組示例代碼:

- (IBAction)groupAction:(id)sender {

? ? NSLog(@"\n\n**************GCD隊列組***************\n");

? ? dispatch_group_t group = dispatch_group_create();

? ? dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

? ? dispatch_group_async(group, queue, ^{

? ? ? ? NSLog(@"隊列組:有一個耗時操作完成!");

? ? });

? ? dispatch_group_async(group, queue, ^{

? ? ? ? NSLog(@"隊列組:有一個耗時操作完成阿浓!");

? ? });

? ? dispatch_group_async(group, queue, ^{

? ? ? NSLog(@"隊列組:有一個耗時操作完成他嚷!");

? ? });

? ? dispatch_group_notify(group, queue, ^{

? ? ? ? NSLog(@"隊列組:前面的耗時操作都完成了,回到主線程進(jìn)行相關(guān)操作");

? ? });

}

打印結(jié)果如下:

隊列組:有一個耗時操作完成芭毙!

隊列組:有一個耗時操作完成筋蓖!

隊列組:有一個耗時操作完成!

隊列組:前面的耗時操作都完成了退敦,回到主線程進(jìn)行相關(guān)操作

至此,GCD的相關(guān)操作內(nèi)容敘述完畢,下面繼續(xù)學(xué)習(xí)NSOperation.

七, NSOperation的理解和使用

No.1: ?NSOperation簡介

NSOperation是基于GCD之上的更高一層封裝,NSOperation需要配合NSOperationQueue來實現(xiàn)多線程.

NSOperation實現(xiàn)多線程步驟如下:

1,創(chuàng)建任務(wù): 先將需要執(zhí)行的操作封裝到NSOperation對象中.

2,創(chuàng)建隊列: 創(chuàng)建NSOperationQueue.

3,將任務(wù)加入到隊列中: 將NSOperation對象添加到NSOperationQueue中

需要注意的是,NAOperation是個抽象類,實際運用時需要使用它的子類,有三種方式:

1,使用子類NSInvocationOperation

2,使用子類NSBlockOperation

3,定義繼承自NSOperation的子類,通過內(nèi)部響應(yīng)的方法來封裝任務(wù).

No.2:NSOperation的三種創(chuàng)建方式

◎NSInvocationOperation的使用

創(chuàng)建NSInvocationd對象,并關(guān)聯(lián)方法,之后start.

-?(void)testNSInvocationOperation?{

????//?創(chuàng)建NSInvocationOperation

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

????//?開始執(zhí)行操作

????[invocationOperation?start];

}


-?(void)invocationOperation?{

????NSLog(@"NSInvocationOperation包含的任務(wù)粘咖,沒有加入隊列========%@",?[NSThread?currentThread]);

}

打印結(jié)果如下,得到結(jié)論: 程序在主線程執(zhí)行,沒有開啟新線程.

這是因為NSOperation多線程的使用需要配合隊列NSOperationQueue,后面會講到NSOperationQueue的使用.

NSInvocationOperation包含的任務(wù),沒有加入隊列========{number?=?1,?name?=?main}

◎NSBlockOperation的使用

把任務(wù)放到NSBlockOperation的block中,然后start.

-?(void)testNSBlockOperation?{

????//?把任務(wù)放到block中

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

????????NSLog(@"NSBlockOperation包含的任務(wù)侈百,沒有加入隊列========%@",?[NSThread?currentThread]);

????}];


????[blockOperation?start];

}

執(zhí)行結(jié)果如下,可以看出:主線程執(zhí)行,沒有開啟新線程.

同樣的,NSBlockOperation可以配合隊列NSOperationQueue來實現(xiàn)多線程.

NSBlockOperation包含的任務(wù)瓮下,沒有加入隊列========{number?=?1,?name?=?main}

但是NSBlockOperation有一個addExecutionBlock:,通過這個方法可以讓NSBlockOperation實現(xiàn)多線程.

-?(void)testNSBlockOperationExecution?{

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

????????NSLog(@"NSBlockOperation運用addExecutionBlock主任務(wù)========%@",?[NSThread?currentThread]);

????}];


????[blockOperation?addExecutionBlock:^{

????????NSLog(@"NSBlockOperation運用addExecutionBlock方法添加任務(wù)1========%@",?[NSThread?currentThread]);

????}];

????[blockOperation?addExecutionBlock:^{

????????NSLog(@"NSBlockOperation運用addExecutionBlock方法添加任務(wù)2========%@",?[NSThread?currentThread]);

????}];

????[blockOperation?addExecutionBlock:^{

????????NSLog(@"NSBlockOperation運用addExecutionBlock方法添加任務(wù)3========%@",?[NSThread?currentThread]);

????}];

????[blockOperation?start];

}

執(zhí)行結(jié)果如下,可以看出,NSBlockOperation創(chuàng)建時block中的任務(wù)是在主線程中執(zhí)行,而運行addExecutionBlock加入的任務(wù)是在子線程執(zhí)行的.

NSBlockOperation運用addExecutionBlock========{number?=?1,?name?=?main}

addExecutionBlock方法添加任務(wù)1========{number?=?3,?name?=?(null)}

addExecutionBlock方法添加任務(wù)3========{number?=?5,?name?=?(null)}

addExecutionBlock方法添加任務(wù)2========{number?=?4,?name?=?(null)}

◎運用繼承自NSOperation的子類

首先我們定義一個繼承自NSOperation的類,然后重寫他的main方法, 之后就可以使用這個子類來進(jìn)行相關(guān)操作了.

/*******************"WHOperation.h"*************************/

#import?@interface?WHOperation?:?NSOperation

@end

/*******************"WHOperation.m"*************************/

#import?"WHOperation.h"

@implementation?WHOperation

-?(void)main?{

????for(int?i?=?0;?i?<?3;?i++)?{

????????NSLog(@"NSOperation的子類WHOperation======%@",[NSThread?currentThread]);

????}

}

@end

/*****************回到主控制器使用WHOperation**********************/

-?(void)testWHOperation?{

????WHOperation?*operation?=?[[WHOperation?alloc]?init];

????[operation?start];

}

運行結(jié)果如下,依然是在主線程執(zhí)行

SOperation的子類WHOperation======{number?=?1,?name?=?main}

NSOperation的子類WHOperation======{number?=?1,?name?=?main}

NSOperation的子類WHOperation======{number?=?1,?name?=?main}

所以NSOperation是需要配合NSOperationQueue來實現(xiàn)多線程的,下面來說一下隊列NSOperationQueue.

No.3: ?隊列NSOperationQueue

NSOperationQueue只有兩種隊列:主隊列、其他隊列.其他隊列包含了串行和并發(fā).

主隊列的創(chuàng)建如下,主隊列上的任務(wù)是在主線程中執(zhí)行的

NSOperationQueue?*mainQueue?=?[NSOperationQueue?mainQueue];

其他隊列(非主隊列)的創(chuàng)建如下,加入到"非主隊列的任務(wù)默認(rèn)就是并發(fā)", 開啟多線程

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

注意:

1,非主隊列(其他隊列)可以實現(xiàn)串行或并行.

2,隊列NSOperationQueue有一個參數(shù)叫最大并發(fā)數(shù): maxConcurrentOperationCount.

3,maxConcurrentOperationCount默認(rèn)為-1,直接并發(fā)執(zhí)行,所以加入到"非主隊列"中的任務(wù)默認(rèn)就是并發(fā),開啟多線程.

4,當(dāng)maxConcurrentOperationCount為1時,則表示不開線程,也就是串行.

5,當(dāng)maxConcurrentOperationCount大于1時,進(jìn)行并發(fā)執(zhí)行.

6,系統(tǒng)對對打并發(fā)數(shù)有一個限制,所以即使程序員把maxConcurrentOperationCount設(shè)置的很大,系統(tǒng)也會自動調(diào)整.所以把最大并發(fā)數(shù)設(shè)置的很大是沒有意義的.

No.4: NSOperation + NSOperationQueue

把任務(wù)加入隊列,這才是NSOperation的常規(guī)使用

◎addOperation添加任務(wù)到隊列

先創(chuàng)建好任務(wù),然后用addOperation:方法把任務(wù)添加到隊列,示例代碼如下:

-?(void)testOperationQueue?{

????//?創(chuàng)建隊列钝域,默認(rèn)并發(fā)

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

????//?創(chuàng)建操作讽坏,NSInvocationOperation

????NSInvocationOperation?*invocationOperation?=?[[NSInvocationOperation?alloc]?initWithTarget:self?selector:@selector(invocationOperationAddOperation)?object:nil];

????//?創(chuàng)建操作,NSBlockOperation

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

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"addOperation把任務(wù)添加到隊列======%@",?[NSThread?currentThread]);

????????}

????}];

? ? [queue?addOperation:invocationOperation];

????[queue?addOperation:blockOperation];

}

-?(void)invocationOperationAddOperation?{

????NSLog(@"invocationOperation===aaddOperation把任務(wù)添加到隊列====%@",?[NSThread?currentThread]);

}

運行結(jié)果如下,可以看出,任務(wù)都是在子線程執(zhí)行的,開啟了新線程!

invocationOperation===addOperation把任務(wù)添加到隊列===={number?=?4,?name?=?(null)}

addOperation把任務(wù)添加到隊列======{number?=?3,?name?=?(null)}

addOperation把任務(wù)添加到隊列======{number?=?3,?name?=?(null)}

addOperation把任務(wù)添加到隊列======{number?=?3,?name?=?(null)}

◎這是一個更方便的把任務(wù)添加到隊列的方法,直接把任務(wù)寫到block中,添加到任務(wù)中.

-?(void)testAddOperationWithBlock?{

????//?創(chuàng)建隊列例证,默認(rèn)并發(fā)

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


????//?添加操作到隊列

????[queue?addOperationWithBlock:^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"addOperationWithBlock把任務(wù)添加到隊列======%@",?[NSThread?currentThread]);

????????}

????}];

}

運行結(jié)果如下,任務(wù)確實是在子線程中執(zhí)行

addOperationWithBlock把任務(wù)添加到隊列======{number?=?3,?name?=?(null)}

addOperationWithBlock把任務(wù)添加到隊列======{number?=?3,?name?=?(null)}

addOperationWithBlock把任務(wù)添加到隊列======{number?=?3,?name?=?(null)}

◎運用最大并發(fā)數(shù)實現(xiàn)串行

上面已經(jīng)說過,可以運用隊列的的屬性maxConcurrentOperationCount(最大并發(fā)數(shù))來實現(xiàn)串行,值需要把它設(shè)置為1就可以了,下面我們通過代碼驗證一下.

-?(void)testMaxConcurrentOperationCount?{

????//?創(chuàng)建隊列路呜,默認(rèn)并發(fā)

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

????//?最大并發(fā)數(shù)為1,串行

????queue.maxConcurrentOperationCount?=?1;

????//?最大并發(fā)數(shù)為2织咧,并發(fā)

//????queue.maxConcurrentOperationCount?=?2;

? ?//?添加操作到隊列

????[queue?addOperationWithBlock:^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"addOperationWithBlock把任務(wù)添加到隊列1======%@",?[NSThread?currentThread]);

????????}

????}];

????//?添加操作到隊列

????[queue?addOperationWithBlock:^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"addOperationWithBlock把任務(wù)添加到隊列2======%@",?[NSThread?currentThread]);

????????}

????}];

????//?添加操作到隊列

????[queue?addOperationWithBlock:^{

????????for(int?i?=?0;?i?<?3;?i++)?{

????????????NSLog(@"addOperationWithBlock把任務(wù)添加到隊列3======%@",?[NSThread?currentThread]);

????????}

????}];

}

運行結(jié)果如下,當(dāng)最大并發(fā)數(shù)為1的時候,雖然開啟了線程,但是任務(wù)是順序執(zhí)行的,所以實現(xiàn)了串行

No.5: NSOperation的其他操作

◎取消隊列NSOperationQueue的所有操作, NSOperationQueue對象方法

-?(void)cancel

◎使隊列暫驼痛校或繼續(xù)

//?暫停隊列

[queue?setSuspended:YES];

◎判斷隊列是否暫停

-?(BOOL)isSuspended

暫停和取消不是立刻當(dāng)前操作,而是等當(dāng)前操作執(zhí)行完之后不再進(jìn)行新的操作.

No.6: NSOperation的操作依賴

NSOperation有一個非常好用的方法, 就是操作依賴.可以從字面意思理解:某一個操作(operetion2)依賴于另一個操作(operation1), 只有當(dāng)operation1執(zhí)行完畢,才能執(zhí)行operation2,這時,就是才做依賴大顯身手的時候了.

-?(void)testAddDependency?{

? ? //?并發(fā)隊列

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

? ? //?操作1

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

????????for(int?i?=?0;?i?<?3;?i++)?{

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

????????}

????}];

? ? //?操作2

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

????????NSLog(@"****operation2依賴于operation1,只有當(dāng)operation1執(zhí)行完畢笙蒙,operation2才會執(zhí)行****");

????????for(int?i?=?0;?i?<?3;?i++)?{

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

????????}

????}];

? ? //?使操作2依賴于操作1

????[operation2?addDependency:operation1];

????//?把操作加入隊列

????[queue?addOperation:operation1];

????[queue?addOperation:operation2];

}

運行結(jié)果如下,操作2總是在操作1之后運行,成功驗證了上面的說法

operation1======{number?=?3,?name?=?(null)}

operation1======{number?=?3,?name?=?(null)}

operation1======{number?=?3,?name?=?(null)}

****operation2依賴于operation1抵屿,只有當(dāng)operation1執(zhí)行完畢,operation2才會執(zhí)行****

operation2======{number?=?4,?name?=?(null)}

operation2======{number?=?4,?name?=?(null)}

operation2======{number?=?4,?name?=?(null)}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末手趣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绿渣,老刑警劉巖朝群,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異中符,居然都是意外死亡姜胖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門淀散,熙熙樓的掌柜王于貴愁眉苦臉地迎上來右莱,“玉大人,你說我怎么就攤上這事档插÷眩” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵郭膛,是天一觀的道長晨抡。 經(jīng)常有香客問我,道長则剃,這世上最難降的妖魔是什么耘柱? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮棍现,結(jié)果婚禮上调煎,老公的妹妹穿的比我還像新娘。我一直安慰自己己肮,他們只是感情好士袄,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著朴肺,像睡著了一般窖剑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戈稿,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天西土,我揣著相機與錄音,去河邊找鬼鞍盗。 笑死需了,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的般甲。 我是一名探鬼主播肋乍,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼敷存!你這毒婦竟也來了墓造?” 一聲冷哼從身側(cè)響起堪伍,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎觅闽,沒想到半個月后帝雇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蛉拙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年尸闸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孕锄。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吮廉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畸肆,到底是詐尸還是另有隱情宦芦,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布恼除,位于F島的核電站踪旷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏豁辉。R本人自食惡果不足惜令野,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望徽级。 院中可真熱鬧气破,春花似錦、人聲如沸餐抢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春旗笔,著一層夾襖步出監(jiān)牢的瞬間珍昨,已是汗流浹背审胚。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工恕沫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绞呈。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓贸人,卻偏偏與公主長得像,于是被迫代替她去往敵國和親佃声。 傳聞我的和親對象是個殘疾皇子艺智,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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