多線程是一個(gè)比較輕量級(jí)的方法來(lái)實(shí)現(xiàn)單個(gè)應(yīng)用程序內(nèi)多個(gè)代碼執(zhí)行路徑
為霍邱等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及霍邱網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)、霍邱網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
在系統(tǒng)級(jí)別內(nèi),程序并排執(zhí)行,程序分配到每個(gè)程序的執(zhí)行時(shí)間是基于該程序的所需時(shí)間和其他程序的所需時(shí)間來(lái)決定的。
然而,在每個(gè)程序內(nèi)部,存在一個(gè)或者多個(gè)執(zhí)行線程,它同時(shí)或在一個(gè)幾乎同時(shí)發(fā)生的方式里執(zhí)行不同的任務(wù)。
概要提示:
iPhone中的線程應(yīng)用并不是無(wú)節(jié)制的,官方給出的資料顯示,iPhone OS下的主線程的堆棧大小是1M,第二個(gè)線程開始就是512KB,并且該值不能通過(guò)編譯器開關(guān)或線程API函數(shù)來(lái)更改,只有主線程有直接修改UI的能力
一、線程概述
有些程序是一條直線,起點(diǎn)到終點(diǎn)——如簡(jiǎn)單的hello world,運(yùn)行打印完,它的生命周期便結(jié)束了,像是曇花一現(xiàn)。
有些程序是一個(gè)圓,不斷循環(huán)直到將它切斷——如操作系統(tǒng),一直運(yùn)行直到你關(guān)機(jī)。
一個(gè)運(yùn)行著的程序就是一個(gè)進(jìn)程或者叫做一個(gè)任務(wù),一個(gè)進(jìn)程至少包含一個(gè)線程,線程就是程序的執(zhí)行流。
Mac和IOS中的程序啟動(dòng),創(chuàng)建好一個(gè)進(jìn)程的同時(shí),一個(gè)線程便開始運(yùn)作,這個(gè)線程叫做主線程。主線成在程序中的位置和其他線程不同,它是其他線程最終的父線程,且所有的界面的顯示操作即AppKit或UIKit的操作必須在主線程進(jìn)行。
系統(tǒng)中每一個(gè)進(jìn)程都有自己獨(dú)立的虛擬內(nèi)存空間,而同一個(gè)進(jìn)程中的多個(gè)線程則公用進(jìn)程的內(nèi)存空間。
每創(chuàng)建一個(gè)新的進(jìn)成,都需要一些內(nèi)存(如每個(gè)線程有自己的stack空間)和消耗一定的CPU時(shí)間。
當(dāng)多個(gè)進(jìn)成對(duì)同一個(gè)資源出現(xiàn)爭(zhēng)奪的時(shí)候需要注意線程安全問(wèn)題
創(chuàng)建線程
創(chuàng)建一個(gè)新的線程就是給進(jìn)程增加一個(gè)執(zhí)行流,所以新建一個(gè)線程需要提供一個(gè)函數(shù)或者方法作為線程的進(jìn)口。
1.使用NSThread
NSThread提供了創(chuàng)建線程的路徑,還可以提供了監(jiān)測(cè)當(dāng)前線程是否是主線程的方法
使用NSThread創(chuàng)建一個(gè)新的線程有兩種方式:
1.創(chuàng)建一個(gè)NSThread的對(duì)象,調(diào)用Start方法——使用一個(gè)目標(biāo)對(duì)象的方法初始化一個(gè)NSThread對(duì)象,或者創(chuàng)建一個(gè)繼承自NSThread的子類,實(shí)現(xiàn)起main方法?,然后在直接創(chuàng)建這個(gè)子類的對(duì)象。
2.使用detachNewThreadSelector:toTarget:withObject:這個(gè)類方法創(chuàng)建一個(gè)子線程,這個(gè)比較直接,直接使用目標(biāo)對(duì)象的方法作為線程啟動(dòng)入口
2.使用NSObject
使用NSObject直接就加入了對(duì)多線程的支持,允許對(duì)象的某個(gè)方法在后臺(tái)運(yùn)行。
[my0bj performSelectorInBackground:@selector(doSomething) withObject:nil];
3.POSIX Thread
由于Mac和IOS都是基于Darwin系統(tǒng),Darwin系統(tǒng)的UNX內(nèi)核,是基于mach和BSD的,繼承了BSD的POSIX接口,所以可以直接使用POSIX線程的相關(guān)接口開實(shí)現(xiàn)線程
創(chuàng)建線程的接口為 pthread_create, 當(dāng)然在創(chuàng)建線程之前可以創(chuàng)建好相關(guān)線程的屬性
——————————————————————————————————————
NSOperation&NSOperationQueue
很多時(shí)候我們使用多線程,需要控制線程的并發(fā)數(shù),畢竟線程也是需要消耗系統(tǒng)資源的,當(dāng)程序中同時(shí)運(yùn)行的線程過(guò)多時(shí),系統(tǒng)必然變慢,所以很多時(shí)候我們會(huì)控制同時(shí)運(yùn)行線程的數(shù)目
NSOperation可以封裝我們的操作,然后將創(chuàng)建好的NSOperation對(duì)象放到NSOperationQueue隊(duì)列中,OperationQueue便開始啟動(dòng)新的線程去執(zhí)行隊(duì)列中的操作,OperationQueue的并發(fā)數(shù)時(shí)可以通過(guò)如下方式進(jìn)行設(shè)置的:
- (void)setMaxConcurrentOperationCount:(NSInteger)count
GCD時(shí)Grand central Dispatch的縮寫,是一系列BSD層面的接口。在mac10.6和IOS4.0以后才引入的
且現(xiàn)在NSOperation和NSOperationQueue的多線程的實(shí)現(xiàn)就是基于GCD的。目前這個(gè)特性也被移植到 FreeBSD上了,可以查看libdispatch這個(gè)開源項(xiàng)目。
dispatch_queue_t p_w_picpathDownloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
當(dāng)然,GCD除了處理多線程外還有很多非常好的功能,其建立在強(qiáng)大的kqueue之上,效率也能夠得到保障。
IOS的多線程,一般分為三種方式:
1,Thread;
2, Cocoa operations;
3, Grand Central Dispatch (GCD) (iOS4 才開始支持)
下面簡(jiǎn)單說(shuō)明一下:
1:NSThread
創(chuàng)建方式主要有兩種:
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];
和
NSThread* myThread = [[NSThread alloc] initWithTarget:self
selector:@selector(myThreadMainMethod:)
object:nil];
[myThread start]; //啟動(dòng)線程這兩種方式的區(qū)別是:前一種一調(diào)用就會(huì)立即創(chuàng)建一個(gè)線程來(lái)做事情;而后一種雖然你 alloc 了也 init了,但是要直到我們手動(dòng)調(diào)用 start 啟動(dòng)線程時(shí)才會(huì)真正去創(chuàng)建線程。這種延遲實(shí)現(xiàn)思想在很多跟資源相關(guān)的地方都有用到。后一種方式我們還可以在啟動(dòng)線程之前,對(duì)線程進(jìn)行配置,比如設(shè)置 stack 大小,線程優(yōu)先級(jí)。
此外還有一種間接的方式:利用NSObject的方法
performSelectorInBackground:withObject: 來(lái)創(chuàng)建一個(gè)線程:
[myObj performSelectorInBackground:@selector(myThreadMainMethod) withObject:nil]; //在后臺(tái)運(yùn)行某一個(gè)方法
其效果與 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一樣的。
2,NSOperation:
官方解釋:The NSOperation class is an abstract class you use to encapsulate the code and data associated with a single task. Because it is abstract, you do not use this class directly but instead subclass or use one of the system-defined subclasses (NSInvocationOperation or NSBlockOperation) to perform the actual task.
并發(fā)執(zhí)行你需要重載如下4個(gè)方法
//執(zhí)行任務(wù)主函數(shù),線程運(yùn)行的入口函數(shù)
-(void)start
//是否允許并發(fā),返回YES,允許并發(fā),返回NO不允許。默認(rèn)返回NO
-(BOOL)isConcurrent
- (BOOL)isExecuting
//是否已經(jīng)完成,這個(gè)必須要重載,不然放在放在NSOperationQueue里的NSOpertaion不能正常釋放。
- (BOOL)isFinished
比如TestNSOperation:NSOperaion 重載上述的4個(gè)方法,
聲明一個(gè)NSOperationQueue,NSOperationQueue *queue = [[[NSOperationQueue alloc ] init]autorelease];
[queue addOperation:testNSoperation];
它會(huì)自動(dòng)調(diào)用TestNSOperation里的start函數(shù),如果需要多個(gè)NSOperation,你需要設(shè)置queue的一些屬性,如果多個(gè)NSOperation之間有依賴關(guān)系,也可以設(shè)置,具體可以參考API文檔。
(2)非并發(fā)執(zhí)行
-(void)main
只需要重載這個(gè)main方法就可以了。
3、 GCD
dispatch_async(dispatch_queue_t queue,dispatch_block_t block);
async表明異步運(yùn)行,block代表的是你要做的事情,queue則是你把任務(wù)交給誰(shuí)來(lái)處理了.之所以程序中會(huì)用到多線程是因?yàn)槌绦蛲鶗?huì)需要讀取數(shù)據(jù),然后更新UI.為了良好的用戶體驗(yàn),讀取數(shù)據(jù)的操作會(huì)傾向于在后臺(tái)運(yùn)行,這樣以避免阻塞主線程.GCD里就有三種queue來(lái)處理。
1. Main queue:
顧名思義,運(yùn)行在主線程,由dispatch_get_main_queue獲得.和ui相關(guān)的就要使用MainQueue.
2.Serial quque(private dispatch queue)
每次運(yùn)行一個(gè)任務(wù),可以添加多個(gè),執(zhí)行次序FIFO. 通常是指程序員生成的.3. Concurrent queue(globaldispatch queue):
可以同時(shí)運(yùn)行多個(gè)任務(wù),每個(gè)任務(wù)的啟動(dòng)時(shí)間是按照加入queue的順序,結(jié)束的順序依賴各自的任務(wù).使用dispatch_get_global_queue獲得.
所以我們可以大致了解使用GCD的框架:
1
2
3
4
5
6
7
dispatch_async(getDataQueue,^{
//獲取數(shù)據(jù),獲得一組后,刷新UI.
dispatch_aysnc(mainQueue, ^{
//UI的更新需在主線程中進(jìn)行
};
}
)
下面 就來(lái)總結(jié)一下這三種多線程方式的區(qū)別吧:
Thread 是這三種范式里面相對(duì)輕量級(jí)的,但也是使用起來(lái)最負(fù)責(zé)的,你需要自己管理thread的生命周期,線程之間的同步。線程共享同一應(yīng)用程序的部分內(nèi)存空間, 它們擁有對(duì)數(shù)據(jù)相同的訪問(wèn)權(quán)限。你得協(xié)調(diào)多個(gè)線程對(duì)同一數(shù)據(jù)的訪問(wèn),一般做法是在訪問(wèn)之前加鎖,這會(huì)導(dǎo)致一定的性能開銷。在 iOS 中我們可以使用多種形式的 thread:
Cocoa threads: 使用NSThread 或直接從 NSObject 的類方法 performSelectorInBackground:withObject: 來(lái)創(chuàng)建一個(gè)線程。如果你選擇thread來(lái)實(shí)現(xiàn)多線程,那么 NSThread 就是官方推薦優(yōu)先選用的方式。
Cocoa operations是基于 Obective-C實(shí)現(xiàn)的,類 NSOperation 以面向?qū)ο蟮姆绞椒庋b了用戶需要執(zhí)行的操作,我們只要聚焦于我們需要做的事情,而不必太操心線程的管理,同步等事情,因?yàn)镹SOperation已經(jīng)為我 們封裝了這些事情。 NSOperation 是一個(gè)抽象基類,我們必須使用它的子類。iOS 提供了兩種默認(rèn)實(shí)現(xiàn):NSInvocationOperation 和 NSBlockOperation。
Grand Central Dispatch (GCD): iOS4 才開始支持,它提供了一些新的特性,以及運(yùn)行庫(kù)來(lái)支持多核并行編程,它的關(guān)注點(diǎn)更高:如何在多個(gè) cpu 上提升效率。最后,既然說(shuō)道多線程的開發(fā),難免會(huì)在多線程之間進(jìn)行通訊;
利用NSObject的一些類方法,可以輕松搞定。
在應(yīng)用程序主線程中做事情:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array
在指定線程中做事情:
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array
在當(dāng)前線程中做事情:
//Invokes a method of the receiver on the current thread using the default mode after a delay.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
performSelector:withObject:afterDelay:inModes:
取消發(fā)送給當(dāng)前線程的某個(gè)消息
cancelPreviousPerformRequestsWithTarget:cancelPreviousPerformRequestsWithTarget:selector:object:
如在我們?cè)谀硞€(gè)線程中下載數(shù)據(jù),下載完成之后要通知主線程中更新界面等等,可以使用如下接口:- (void)myThreadMainMethod
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// to do something in your thread job
...
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
[pool release];
}