iOS多線程討論

馬云催淚勵(lì)志演講 為什么你還是窮人

開發(fā)中為了更好的用戶體驗(yàn)我們會(huì)用到多線程芬沉。

主要討論三中創(chuàng)建多線程的方法:NSThread,GCD阁猜,NSOperation 丸逸。

NSThread

從命名來看這是一個(gè)封裝好的類,它的生命周期需要我們手動(dòng)管理剃袍。常用的創(chuàng)建方法

1黄刚、通過類方法創(chuàng)建并自動(dòng)啟動(dòng):

+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

2、通過實(shí)例方法創(chuàng)建手動(dòng)啟動(dòng):

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

[newThread setName:@"threadName"];//自定義線程名稱

[newThread start];//啟動(dòng)

[newThread cancel];//取消

關(guān)于NSThread我們實(shí)際開發(fā)中常用的方法是:

+ (NSThread *)currentThread;//獲取當(dāng)前線程信息

+ (NSThread *)mainThread;//獲取主線程信息

+ (void)sleepForTimeInterval:(NSTimeInterval)time;//調(diào)試時(shí)用的睡眠

GCD

GCD為Grand Central Dispatch首字母縮寫民效,是Apple開發(fā)的一個(gè)多核編程的解決方案憔维。起源于Mac OS X 10.6,在iOS4.0被引入。GCD會(huì)自動(dòng)管理線程的生命周期畏邢,采用C語言實(shí)現(xiàn)业扒,通過Block將執(zhí)行體傳入。

1舒萎、GCD中有三種隊(duì)列類型:

①the main queue:

獲取方式dispatch_queue_t mainQueue = dispatch_get_main_queue();提交到該隊(duì)列中的任務(wù)會(huì)在主線程執(zhí)行程储,為一個(gè)串行隊(duì)列。

②the global queue:

獲取方式??? dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);全局隊(duì)列是并發(fā)隊(duì)列臂寝,并由整個(gè)進(jìn)程共享章鲤,第一個(gè)參數(shù)為選擇隊(duì)列,參數(shù)可選為

#define DISPATCH_QUEUE_PRIORITY_HIGH 2//高

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0//中咆贬,默認(rèn)

#define DISPATCH_QUEUE_PRIORITY_LOW (-2)//低

#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN//后臺(tái)

③the user defined queue:

獲取方式:第一個(gè)參數(shù)為名稱败徊,debug時(shí)會(huì)顯示

dispatch_queue_t serialQueue =dispatch_queue_create("com.serial.queue",NULL);//串行

dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);//串行

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);//并行

2、應(yīng)用:

①關(guān)于dispatch_async---添加任務(wù)到一個(gè)隊(duì)列并不等待任務(wù)完成素征,而是立即繼續(xù)其他任務(wù)集嵌。

//首先相對(duì)當(dāng)前語句所在的線程來說是異步提交,將任務(wù)提交到default級(jí)別的全局隊(duì)列中返回御毅,block等待default隊(duì)列FIFO順序執(zhí)行

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[self goDoSomethingLongTask];//在default隊(duì)列中執(zhí)行完,此時(shí)不會(huì)繼續(xù)往下執(zhí)行根欧,除非當(dāng)前語句塊執(zhí)行完成

dispatch_async(dispatch_get_main_queue(), ^{//異步提交任務(wù)到main_queue

[textField setStringValue:@"Done doing something long and involved"];//在main中執(zhí)行當(dāng)前語句塊

});

});

②關(guān)于dispatch_sync---添加任務(wù)到一個(gè)隊(duì)列并等待直到任務(wù)完成,用于等待提交任務(wù)的結(jié)果,才能繼續(xù)執(zhí)行下面的代碼塊的情況端蛆。

dispatch_sync一般應(yīng)用在并發(fā)隊(duì)列:這才是做同步工作的好選擇凤粗。

在主線程中執(zhí)行如下代碼塊:

//首先相對(duì)當(dāng)前語句所在的線程來說是同步提交,將任務(wù)提交到main queue中今豆,等待main queue執(zhí)行該block-----這里會(huì)造成阻塞嫌拣,因?yàn)閟ync已經(jīng)阻塞當(dāng)前線程,等待block在所提交的線程執(zhí)行完成后才放開阻塞的線程呆躲,即A被阻塞等待任務(wù)執(zhí)行完异逐,任務(wù)又等A的調(diào)度執(zhí)行。

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog("sync - %@", NSThread.currentThread());

});

③關(guān)于dispatch_group

演示一個(gè)應(yīng)用:

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//獲得一個(gè)異步隊(duì)列

dispatch_group_t group = dispatch_group_create();//新建一個(gè)group隊(duì)列

for(idobj in array){//for循環(huán)遍歷 每次異步方式提交任務(wù)block到queue插掂,

dispatch_group_async(group, queue, ^{

[selfdoSomethingIntensiveWithbj];

});

}

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//group進(jìn)入永久阻塞等待灰瞻,直到之前任務(wù)完成

dispatch_release(group);

[selfdoSomethingWith:array];//group完成后,執(zhí)行此

優(yōu)化:將最后的任務(wù)放到異步線程中執(zhí)行

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_group_t group = dispatch_group_create();

for(idobj in array){//近乎平行執(zhí)行

dispatch_group_async(group, queue, ^{

[selfdoSomethingIntensiveWithbj];

});

}

//之前任務(wù)完成后會(huì)被notify通知執(zhí)行

dispatch_group_notify(group, queue, ^{

[selfdoSomethingWith:array];

});

dispatch_release(group);

④關(guān)于dispatch_apply辅甥。

同步執(zhí)行dispath_apply代碼塊酝润,調(diào)用單一block多次,并平行運(yùn)算璃弄,然后等待所有運(yùn)算結(jié)束要销,需保證block中線程安全

dispatch_apply(array.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {

[NSThread sleepForTimeInterval:1];

NSLog(@"---%@",array[index]);

});

[self say];//最后執(zhí)行

⑤關(guān)于dispatch_barrier。在并發(fā)隊(duì)列中扮演一個(gè)串行式的瓶頸作用夏块。

能夠確保提交的block在被執(zhí)行的特定時(shí)間上是指定隊(duì)列上唯一被執(zhí)行的條目疏咐!由于隊(duì)列是FIFO的,換句話說所有先于該barrier block的條目一定能再這個(gè)block執(zhí)行前完成脐供。等完成該block后隊(duì)列恢復(fù)默認(rèn)狀態(tài)凳鬓。

應(yīng)用--讀寫問題:

線程不能保證安全

- (void)addPhoto:(Photo *)photo

{

if (photo) {

[_photosArray addObject:photo];

dispatch_async(dispatch_get_main_queue(), ^{

[self postContentAddedNotification];

}

修改成一個(gè)線程安全的寫操作

-(void)addPhoto:(Photo *)photo

{

if (photo) { // 1判斷非空

dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2 barrier方式異步提交代碼塊到queue中

[_photosArray addObject:photo]; // 3等之前提交的都執(zhí)行完后,只有當(dāng)前代碼塊在queue中執(zhí)行患民,barrier提交確保線程安全的缩举!

dispatch_async(dispatch_get_main_queue(), ^{ // 4通知

[self postContentAddedNotification];

});

});

}

}

讀操作

不能提供任何保護(hù)來對(duì)抗當(dāng)一個(gè)線程調(diào)用讀方法的同時(shí)另一個(gè)線程調(diào)用寫方法

- (NSArray *)photos

{

return [NSArray arrayWithArray:_photosArray];

}

修改為:

- (NSArray *)photos

{

__block NSArray *array; // 1

dispatch_sync(self.concurrentPhotoQueue, ^{ // 2同步提交等待,block代碼塊在queue中執(zhí)行那個(gè)完成匹颤,因?yàn)閷懖僮魇莃arrier提交保證了執(zhí)行時(shí)只有寫操作自己在queue中仅孩!

array = [NSArray arrayWithArray:_photosArray]; // 3等待queue調(diào)度執(zhí)行代碼塊

});

return array;

}

⑥dispatch_once,常用于創(chuàng)建線程安全的單例印蓖。

+ (TransHistoryInstance *)sharedInstance

{

static TransHistoryInstance *instance;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

instance = [[self alloc] init];

});

return instance;

}

⑦dispatch_after? 用于延遲操作

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

//code to be executed after a specified delay

});

⑧ dispatch_semaphore信號(hào)量 阻塞方式的一種辽慕,為0等待,否則減一繼續(xù)

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);//初始化為1,保證不會(huì)一開始被阻塞赦肃。

NSMutableArray *array = [NSMutableArray array];

for (int index = 0; index < 100000; index++) {

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

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//如果semaphore計(jì)數(shù)大于等于1.計(jì)數(shù)-1溅蛉,返回公浪,程序繼續(xù)向下運(yùn)行。如果計(jì)數(shù)為0船侧,則等待

NSLog(@"addd :%d", index);

[array addObject:[NSNumber numberWithInt:index]];

// Increment the counting semaphore.

dispatch_semaphore_signal(semaphore);//加1});

}

NSOperation And NSOperationQueue

1欠气、NSOperation? 是一個(gè)抽象類,首先繼承NSOperation? 重寫main函數(shù) 在main中創(chuàng)建一個(gè)autoreleasepool 在autoreleasepool中添加代碼镜撩。

①start開始:通常不重載該方法预柒,如果重載,必須關(guān)注像isExecuting, isFinished, isConcurrent, and isReady這些屬性袁梗。

將一個(gè)operation添加到NSOperationQueue隊(duì)列后宜鸯,隊(duì)列會(huì)自動(dòng)調(diào)用該operation的start方法,執(zhí)行完一些準(zhǔn)備工作后遮怜,執(zhí)行main函數(shù)淋袖。

但是,如果手動(dòng)觸發(fā)start方法锯梁,并沒有添加到NSOperationQueue隊(duì)列中适贸,該operation將會(huì)在主線程中運(yùn)行。

②dependency依賴:兩個(gè)operation之間可以建立依賴關(guān)系涝桅。

NSBlockOperation *downloadOperation = [NSBlockOperation blockOperationWithBlock:^{

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

}];

NSBlockOperation *storeOperation = [NSBlockOperation blockOperationWithBlock:^{

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

}];

[storeOperation addDependency:downloadOperation];//store依賴download 等到download操作的isFinished 為YES時(shí)拜姿,才開始執(zhí)行 [storeOperation removeDependency:downloadOperation];//移除依賴關(guān)系

③priority優(yōu)先級(jí): 當(dāng)你增加一個(gè)操作到隊(duì)列,在調(diào)用他們的“start”之前冯遂,NSOperationQueue會(huì)查找所有的操作蕊肥,優(yōu)先級(jí)高的操作優(yōu)先執(zhí)行。同級(jí)的操作按照提交到隊(duì)列的順序執(zhí)行(FIFO).

[downloadOperation setQueuePriority:NSOperationQueuePriorityHigh];//設(shè)置優(yōu)先級(jí)

④completion block完成后的響應(yīng)block

[blockOperation setCompletionBlock:^{

//code不一定在主線程中

}];

2蛤肌、NSOperationQueue? 是一個(gè)隊(duì)列壁却。一個(gè)Queue隊(duì)列中可以有多個(gè)線程,不同的線程并發(fā)的執(zhí)行裸准。隊(duì)列中并發(fā)操作的數(shù)目小于等于所設(shè)置的最大運(yùn)行數(shù)展东。要定期檢查(昂貴操作前后)isCancelled,確保盡快終止操作炒俱。

舉個(gè)小栗子:

@implementation myOperation

//一般不需要重載 如果重載盐肃,必須關(guān)注像isExecuting, isFinished, isConcurrent, and isReady這些屬性。

//- (void)start{

//

//}

- (void)main

{

@autoreleasepool {

NSLog(@"isMainThread-%d",[NSThread isMainThread]);

if (self.isCancelled)

return;

NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://images.freeimages.com/images/previews/3a2/fall-in-ontario-1056162.jpg"]];

if (self.isCancelled) {

imageData = nil;

return;

}

// do something about imageData

if (self.isCancelled)

return;

// report to somebody on main thread

}

}

使用:

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

queue.name = @"my Queue";

queue.maxConcurrentOperationCount = 3;//同時(shí)執(zhí)行的最大并發(fā)數(shù)

for(int i = 0; i < 100;i++){

myOperation *operation = [[myOperation alloc] init];

[queue addOperation:operation];//不一定會(huì)立即觸發(fā)

}

關(guān)于GCD和NSOperation各有所長(zhǎng)权悟,以具體需求取舍吧砸王。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市峦阁,隨后出現(xiàn)的幾起案子谦铃,更是在濱河造成了極大的恐慌,老刑警劉巖榔昔,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驹闰,死亡現(xiàn)場(chǎng)離奇詭異瘪菌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嘹朗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門师妙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人骡显,你說我怎么就攤上這事≡啵” “怎么了惫谤?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)珠洗。 經(jīng)常有香客問我溜歪,道長(zhǎng),這世上最難降的妖魔是什么许蓖? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任蝴猪,我火速辦了婚禮,結(jié)果婚禮上膊爪,老公的妹妹穿的比我還像新娘自阱。我一直安慰自己,他們只是感情好米酬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布沛豌。 她就那樣靜靜地躺著,像睡著了一般赃额。 火紅的嫁衣襯著肌膚如雪加派。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天跳芳,我揣著相機(jī)與錄音芍锦,去河邊找鬼。 笑死飞盆,一個(gè)胖子當(dāng)著我的面吹牛娄琉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吓歇,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼车胡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了照瘾?” 一聲冷哼從身側(cè)響起匈棘,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎析命,沒想到半個(gè)月后主卫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逃默,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年簇搅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了完域。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘩将,死狀恐怖吟税,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情姿现,我是刑警寧澤肠仪,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站备典,受9級(jí)特大地震影響异旧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜提佣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一吮蛹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拌屏,春花似錦潮针、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至务唐,卻和暖如春雳攘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枫笛。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工吨灭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刑巧。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓喧兄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親啊楚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吠冤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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