多線程編程4 - GCD

一、簡介

iOS所 有實(shí)現(xiàn)多線程的方案中,GCD應(yīng)該是最有魅力的,因?yàn)镚CD本身是蘋果公司為多核的并行運(yùn)算提出的解決方案。GCD在工作時(shí)會(huì)自動(dòng)利用更多的處理器核心, 以充分利用更強(qiáng)大的機(jī)器。GCD是Grand Central Dispatch的簡稱,它是基于C語言的。如果使用GCD,完全由系統(tǒng)管理線程,我們不需要編寫線程代碼。只需定義想要執(zhí)行的任務(wù),然后添加到適當(dāng)?shù)恼{(diào)度隊(duì)列(dispatch queue)。GCD會(huì)負(fù)責(zé)創(chuàng)建線程和調(diào)度你的任務(wù),系統(tǒng)直接提供線程管理

二、調(diào)度隊(duì)列(dispath queue)

1.GCD的一個(gè)重要概念是隊(duì)列,它的核心理念:將長期運(yùn)行的任務(wù)拆分成多個(gè)工作單元,并將這些單元添加到dispath queue中,系統(tǒng)會(huì)為我們管理這些dispath queue,為我們在多個(gè)線程上執(zhí)行工作單元,我們不需要直接啟動(dòng)和管理后臺線程。

2.系統(tǒng)提供了許多預(yù)定義的dispath queue,包括可以保證始終在主線程上執(zhí)行工作的dispath queue。也可以創(chuàng)建自己的dispath queue,而且可以創(chuàng)建任意多個(gè)。GCD的dispath queue嚴(yán)格遵循FIFO(先進(jìn)先出)原則,添加到dispath queue的工作單元將始終按照加入dispath queue的順序啟動(dòng)。

3.dispatch queue按先進(jìn)先出的順序,串行或并發(fā)地執(zhí)行任務(wù)

1> serial dispatch queue一次只能執(zhí)行一個(gè)任務(wù), 當(dāng)前任務(wù)完成才開始出列并啟動(dòng)下一個(gè)任務(wù)

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

三、創(chuàng)建和管理dispatch queue

1.獲得全局并發(fā)Dispatch Queue (concurrent dispatch queue)

1> 并發(fā)dispatch queue可以同時(shí)并行地執(zhí)行多個(gè)任務(wù),不過并發(fā)queue仍然按先進(jìn)先出的順序來啟動(dòng)任務(wù)。并發(fā)queue會(huì)在之前的任務(wù)完成之前就出列下一個(gè)任務(wù)并開 始執(zhí)行。并發(fā)queue同時(shí)執(zhí)行的任務(wù)數(shù)量會(huì)根據(jù)應(yīng)用和系統(tǒng)動(dòng)態(tài)變化,各種因素包括:可用核數(shù)量、其它進(jìn)程正在執(zhí)行的工作數(shù)量、其它串行dispatch queue中優(yōu)先任務(wù)的數(shù)量等.

2> 系統(tǒng)給每個(gè)應(yīng)用提供三個(gè)并發(fā)dispatch queue,整個(gè)應(yīng)用內(nèi)全局共享,三個(gè)queue的區(qū)別是優(yōu)先級。你不需要顯式地創(chuàng)建這些queue,使用dispatch_get_global_queue函數(shù)來獲取這三個(gè)queue:

//?獲取默認(rèn)優(yōu)先級的全局并發(fā)dispatch?queue

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

第一個(gè)參數(shù)用于指定優(yōu)先級,分別使用DISPATCH_QUEUE_PRIORITY_HIGH和DISPATCH_QUEUE_PRIORITY_LOW兩個(gè)常量來獲取高和低優(yōu)先級的兩個(gè)queue;第二個(gè)參數(shù)目前未使用到,默認(rèn)0即可

3> 雖然dispatch queue是引用計(jì)數(shù)的對象,但你不需要retain和release全局并發(fā)queue。因?yàn)檫@些queue對應(yīng)用是全局的,retain和 release調(diào)用會(huì)被忽略。你也不需要存儲(chǔ)這三個(gè)queue的引用,每次都直接調(diào)用dispatch_get_global_queue獲得queue 就行了。

2.創(chuàng)建串行Dispatch Queue (serial dispatch queue)

1> 應(yīng)用的任務(wù)需要按特定順序執(zhí)行時(shí),就需要使用串行Dispatch Queue,串行queue每次只能執(zhí)行一個(gè)任務(wù)。你可以使用串行queue來替代鎖,保護(hù)共享資源 或可變的數(shù)據(jù)結(jié)構(gòu)。和鎖不一樣的是,串行queue確保任務(wù)按可預(yù)測的順序執(zhí)行。而且只要你異步地提交任務(wù)到串行queue,就永遠(yuǎn)不會(huì)產(chǎn)生死鎖

2> 你必須顯式地創(chuàng)建和管理所有你使用的串行queue,應(yīng)用可以創(chuàng)建任意數(shù)量的串行queue,但不要為了同時(shí)執(zhí)行更多任務(wù)而創(chuàng)建更多的串行queue。如果你需要并發(fā)地執(zhí)行大量任務(wù),應(yīng)該把任務(wù)提交到全局并發(fā)queue

3> 利用dispatch_queue_create函數(shù)創(chuàng)建串行queue,兩個(gè)參數(shù)分別是queue名和一組queue屬性

dispatch_queue_t?queue;

queue?=?dispatch_queue_create("cn.itcast.queue",?NULL);

3.運(yùn)行時(shí)獲得公共Queue

GCD提供了函數(shù)讓應(yīng)用訪問幾個(gè)公共dispatch queue:

1> 使用dispatch_get_current_queue函數(shù)作為調(diào)試用途,或者測試當(dāng)前queue的標(biāo)識。在block對象中調(diào)用這個(gè)函數(shù)會(huì)返回 block提交到的queue(這個(gè)時(shí)候queue應(yīng)該正在執(zhí)行中)。在block對象之外調(diào)用這個(gè)函數(shù)會(huì)返回應(yīng)用的默認(rèn)并發(fā)queue。

2> 使用dispatch_get_main_queue函數(shù)獲得應(yīng)用主線程關(guān)聯(lián)的串行dispatch queue

3> 使用dispatch_get_global_queue來獲得共享的并發(fā)queue

4.Dispatch Queue的內(nèi)存管理

1> Dispatch Queue和其它dispatch對象(還有dispatch source)都是引用計(jì)數(shù)的數(shù)據(jù)類型。當(dāng)你創(chuàng)建一個(gè)串行dispatch queue時(shí),初始引用計(jì)數(shù)為 1,你可以使用dispatch_retain和dispatch_release函數(shù)來增加和減少引用計(jì)數(shù)。當(dāng)引用計(jì)數(shù)到達(dá) 0 時(shí),系統(tǒng)會(huì)異步地銷毀這個(gè)queue

2> 對dispatch對象(如dispatch queue)retain和release 是很重要的,確保它們被使用時(shí)能夠保留在內(nèi)存中。和OC對象一樣,通用的規(guī)則是如果使用一個(gè)傳遞過來的queue,你應(yīng)該在使用前retain,使用完之 后release

3> 你不需要retain或release全局dispatch queue,包括全局并發(fā)dispatch queue和main dispatch queue

4> 即使你實(shí)現(xiàn)的是自動(dòng)垃圾收集的應(yīng)用,也需要retain和release創(chuàng)建的dispatch queue和其它dispatch對象。GCD 不支持垃圾收集模型來回收內(nèi)存

四、添加任務(wù)到queue

要執(zhí)行一個(gè)任務(wù),你需要將它添加到一個(gè)適當(dāng)?shù)膁ispatch queue,你可以單個(gè)或按組來添加,也可以同步或異步地執(zhí)行一個(gè)任務(wù),也。一旦進(jìn)入到queue,queue會(huì)負(fù)責(zé)盡快地執(zhí)行你的任務(wù)。一般可以用一個(gè)block來封裝任務(wù)內(nèi)容。

1.添加單個(gè)任務(wù)到queue

1> 異步添加任務(wù)

你可以異步或同步地添加一個(gè)任務(wù)到Queue,盡可能地使用 dispatch_async或dispatch_async_f函數(shù)異步地調(diào)度任務(wù)。因?yàn)樘砑尤蝿?wù)到Queue中時(shí),無法確定這些代碼什么時(shí)候能夠執(zhí) 行。因此異步地添加block或函數(shù),可以讓你立即調(diào)度這些代碼的執(zhí)行,然后調(diào)用線程可以繼續(xù)去做其它事情。特別是應(yīng)用主線程一定要異步地 dispatch 任務(wù),這樣才能及時(shí)地響應(yīng)用戶事件

2> 同步添加任務(wù)

少數(shù)時(shí)候你可能希望同步地調(diào)度任務(wù),以避免競爭條件或其它同步錯(cuò)誤。 使用dispatch_sync和dispatch_sync_f函數(shù)同步地添加任務(wù)到Queue,這兩個(gè)函數(shù)會(huì)阻塞當(dāng)前調(diào)用線程,直到相應(yīng)任務(wù)完成執(zhí)行。注意:絕對不要在任務(wù)中調(diào)用 dispatch_sync或dispatch_sync_f函數(shù),并同步調(diào)度新任務(wù)到當(dāng)前正在執(zhí)行的 queue。對于串行queue這一點(diǎn)特別重要,因?yàn)檫@樣做肯定會(huì)導(dǎo)致死鎖;而并發(fā)queue也應(yīng)該避免這樣做。

3> 代碼演示

[java]view plaincopy

//?調(diào)用前,查看下當(dāng)前線程

NSLog(@"當(dāng)前調(diào)用線程:%@",?[NSThread?currentThread]);

//?創(chuàng)建一個(gè)串行queue

dispatch_queue_t?queue?=?dispatch_queue_create("cn.itcast.queue",?NULL);

dispatch_async(queue,?^{

NSLog(@"開啟了一個(gè)異步任務(wù),當(dāng)前線程:%@",?[NSThread?currentThread]);

});

dispatch_sync(queue,?^{

NSLog(@"開啟了一個(gè)同步任務(wù),當(dāng)前線程:%@",?[NSThread?currentThread]);

});

//?銷毀隊(duì)列

dispatch_release(queue);

打印信息:

2013-02-03?09:03:37.348?thread[6491:c07]?當(dāng)前調(diào)用線程:{name?=?(null),?num?=?1}

2013-02-03?09:03:37.349?thread[6491:1e03]?開啟了一個(gè)異步任務(wù),當(dāng)前線程:{name?=?(null),?num?=?3}

2013-02-03?09:03:37.350?thread[6491:c07]?開啟了一個(gè)同步任務(wù),當(dāng)前線程:{name?=?(null),?num?=?1}

2.并發(fā)地執(zhí)行循環(huán)迭代

如果你使用循環(huán)執(zhí)行固定次數(shù)的迭代, 并發(fā)dispatch queue可能會(huì)提高性能。

例如下面的for循環(huán):

int?i;

int?count?=?10;

for?(i?=?0;?i?<?count;?i++)?{

printf("%d??",i);

}

1> 如果每次迭代執(zhí)行的任務(wù)與其它迭代獨(dú)立無關(guān),而且循環(huán)迭代執(zhí)行順序也無關(guān)緊要的話,你可以調(diào)用dispatch_apply或 dispatch_apply_f函數(shù)來替換循環(huán)。這兩個(gè)函數(shù)為每次循環(huán)迭代將指定的block或函數(shù)提交到queue。當(dāng)dispatch到并發(fā) queue時(shí),就有可能同時(shí)執(zhí)行多個(gè)循環(huán)迭代。用dispatch_apply或dispatch_apply_f時(shí)你可以指定串行或并發(fā) queue。并發(fā)queue允許同時(shí)執(zhí)行多個(gè)循環(huán)迭代,而串行queue就沒太大必要使用了。

下面代碼使用dispatch_apply替換了for循環(huán),你傳遞的block必須包含一個(gè)size_t類型的參數(shù),用來標(biāo)識當(dāng)前循環(huán)迭代。第一次迭代這個(gè)參數(shù)值為0,最后一次值為count - 1

//?獲得全局并發(fā)queue

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

size_t?count?=?10;

dispatch_apply(count,?queue,?^(size_t?i)?{

printf("%zd?",?i);

});

//?銷毀隊(duì)列

dispatch_release(queue);

打印信息:

1?2?0?3?4?5?6?7?8?9

可以看出,這些迭代是并發(fā)執(zhí)行的

和普通for循環(huán)一樣,dispatch_apply和 dispatch_apply_f函數(shù)也是在所有迭代完成之后才會(huì)返回,因此這兩個(gè)函數(shù)會(huì)阻塞當(dāng)前線程,主線程中調(diào)用這兩個(gè)函數(shù)必須小心,可能會(huì)阻止事件 處理循環(huán)并無法響應(yīng)用戶事件。所以如果循環(huán)代碼需要一定的時(shí)間執(zhí)行,可以考慮在另一個(gè)線程中調(diào)用這兩個(gè)函數(shù)。如果你傳遞的參數(shù)是串行queue,而且正是 執(zhí)行當(dāng)前代碼的queue,就會(huì)產(chǎn)生死鎖。

3.在主線程中執(zhí)行任務(wù)

1> GCD提供一個(gè)特殊的dispatch queue,可以在應(yīng)用的主線程中執(zhí)行任務(wù)。只要應(yīng)用主線程設(shè)置了run loop(由CFRunLoopRef類型或NSRunLoop對象管理),就會(huì)自動(dòng)創(chuàng)建這個(gè)queue,并且最后會(huì)自動(dòng)銷毀。非Cocoa應(yīng)用如果不顯 式地設(shè)置run loop, 就必須顯式地調(diào)用dispatch_main函數(shù)來顯式地激活這個(gè)dispatch queue,否則雖然你可以添加任務(wù)到queue,但任務(wù)永遠(yuǎn)不會(huì)被執(zhí)行。

2> 調(diào)用dispatch_get_main_queue函數(shù)獲得應(yīng)用主線程的dispatch queue,添加到這個(gè)queue的任務(wù)由主線程串行化執(zhí)行

3> 代碼實(shí)現(xiàn),比如異步下載圖片后,回到主線程顯示圖片

//?異步下載圖片

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

NSURL?*url?=?[NSURL?URLWithString:@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg"];

UIImage?*image?=?[UIImage?imageWithData:[NSData?dataWithContentsOfURL:url]];

//?回到主線程顯示圖片

dispatch_async(dispatch_get_main_queue(),?^{

self.imageView.image?=?image;

});

});

4.任務(wù)中使用Objective-C對象

GCD支持Cocoa內(nèi)存管理機(jī)制,因此可以在提交到queue的 block中自由地使用Objective-C對象。每個(gè)dispatch queue維護(hù)自己的autorelease pool確保釋放autorelease對象,但是queue不保證這些對象實(shí)際釋放的時(shí)間。如果應(yīng)用消耗大量內(nèi)存,并且創(chuàng)建大量autorelease 對象,你需要?jiǎng)?chuàng)建自己的autorelease pool,用來及時(shí)地釋放不再使用的對象。

五、暫停和繼續(xù)queue

我們可以使用dispatch_suspend函數(shù)暫 停一個(gè)queue以阻止它執(zhí)行block對象;使用dispatch_resume函數(shù)繼續(xù)dispatch queue。調(diào)用dispatch_suspend會(huì)增加queue的引用計(jì)數(shù),調(diào)用dispatch_resume則減少queue的引用計(jì)數(shù)。當(dāng)引用 計(jì)數(shù)大于0時(shí),queue就保持掛起狀態(tài)。因此你必須對應(yīng)地調(diào)用suspend和resume函數(shù)。掛起和繼續(xù)是異步的,而且只在執(zhí)行block之間(比 如在執(zhí)行一個(gè)新的block之前或之后)生效。掛起一個(gè)queue不會(huì)導(dǎo)致正在執(zhí)行的block停止。

六、Dispatch Group的使用

假設(shè)有這樣一個(gè)需求:從網(wǎng)絡(luò)上下載兩張不同的圖片,然后顯示到不同的UIImageView上去,一般可以這樣實(shí)現(xiàn)

//?根據(jù)url獲取UIImage

-?(UIImage?*)imageWithURLString:(NSString?*)urlString?{

NSURL?*url?=?[NSURL?URLWithString:urlString];

NSData?*data?=?[NSData?dataWithContentsOfURL:url];

return?[UIImage?imageWithData:data];

}

-?(void)downloadImages?{

//?異步下載圖片

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

//?下載第一張圖片

NSString?*url1?=?@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";

UIImage?*image1?=?[self?imageWithURLString:url1];

//?下載第二張圖片

NSString?*url2?=?@"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";

UIImage?*image2?=?[self?imageWithURLString:url2];

//?回到主線程顯示圖片

dispatch_async(dispatch_get_main_queue(),?^{

self.imageView1.image?=?image1;

self.imageView2.image?=?image2;

});

});

}

雖然這種方案 可以解決問題,但其實(shí)兩張圖片的下載過程并不需要按順序執(zhí)行,并發(fā)執(zhí)行它們可以提高執(zhí)行速度。有個(gè)注意點(diǎn)就是必須等兩張圖片都下載完畢后才能回到主線程顯 示圖片。Dispatch Group能夠在這種情況下幫我們提升性能。下面先看看Dispatch Group的用處:

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

下面用Dispatch Group優(yōu)化上面的代碼:

//?根據(jù)url獲取UIImage

-?(UIImage?*)imageWithURLString:(NSString?*)urlString?{

NSURL?*url?=?[NSURL?URLWithString:urlString];

NSData?*data?=?[NSData?dataWithContentsOfURL:url];

//?這里并沒有自動(dòng)釋放UIImage對象

return?[[UIImage?alloc]?initWithData:data];

}

-?(void)downloadImages?{

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

//?異步下載圖片

dispatch_async(queue,?^{

//?創(chuàng)建一個(gè)組

dispatch_group_t?group?=?dispatch_group_create();

__block?UIImage?*image1?=?nil;

__block?UIImage?*image2?=?nil;

//?關(guān)聯(lián)一個(gè)任務(wù)到group

dispatch_group_async(group,?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

//?下載第一張圖片

NSString?*url1?=?@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";

image1?=?[self?imageWithURLString:url1];

});

//?關(guān)聯(lián)一個(gè)任務(wù)到group

dispatch_group_async(group,?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

//?下載第一張圖片

NSString?*url2?=?@"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";

image2?=?[self?imageWithURLString:url2];

});

//?等待組中的任務(wù)執(zhí)行完畢,回到主線程執(zhí)行block回調(diào)

dispatch_group_notify(group,?dispatch_get_main_queue(),?^{

self.imageView1.image?=?image1;

self.imageView2.image?=?image2;

//?千萬不要在異步線程中自動(dòng)釋放UIImage,因?yàn)楫?dāng)異步線程結(jié)束,異步線程的自動(dòng)釋放池也會(huì)被銷毀,那么UIImage也會(huì)被銷毀

//?在這里釋放圖片資源

[image1?release];

[image2?release];

});

//?釋放group

dispatch_release(group);

});

}

dispatch_group_notify函數(shù)用來指定一個(gè)額外的block,該block將在group中所有任務(wù)完成后執(zhí)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,802評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,762評論 2 372

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

  • 原文: 多線程編程4 - GCD 一、簡介在iOS所有實(shí)現(xiàn)多線程的方案中,GCD應(yīng)該是最有魅力的,因?yàn)镚CD本身是...
    難卻卻閱讀 154評論 0 0
  • Dispatch Queues dispatch queues是執(zhí)行任務(wù)的強(qiáng)大工具,允許你同步或異步地執(zhí)行任意代碼...
    YangPu閱讀 658評論 0 4
  • 一、簡介 在iOS所有實(shí)現(xiàn)多線程的方案中,GCD應(yīng)該是最有魅力的,因?yàn)镚CD本身是蘋果公司為多核的并行運(yùn)算提出的解...
    Alanxx閱讀 360評論 0 1
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時(shí)候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似。...
    Dely閱讀 9,257評論 21 42
  • 從哪說起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個(gè)最簡單的問題,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,803評論 1 17