我們介紹多線程首先我們需要一些基礎(chǔ)知識(shí)一下我們一一介紹
進(jìn)程:指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,每個(gè)進(jìn)程是獨(dú)立,每個(gè)京城都運(yùn)行在其專用受保護(hù)的內(nèi)存空間。這也就是說多進(jìn)程要比多線程健壯我們之后介紹。
線程:是進(jìn)程的基本執(zhí)行單元,一個(gè)進(jìn)程的所有任務(wù)都在線程中執(zhí)行,在一個(gè)線程中任務(wù)都是串行(順序執(zhí)行)。
線程通信:在一個(gè)進(jìn)程中,線程往往不是孤立存在的,多個(gè)線程之間需要經(jīng)常進(jìn)行通信,比如一個(gè)線程傳遞數(shù)據(jù)給另一個(gè)線程,執(zhí)行完一個(gè)線程的任務(wù)后在執(zhí)行另一個(gè)線程。
多線程:一個(gè)進(jìn)程中開啟多條線程,每條線程并發(fā)(同時(shí))執(zhí)行不同的任務(wù),多線程可以提高程序的執(zhí)行效率。
原理
同一時(shí)間,CPU只能處理1條線程,只有1條線程在工作(執(zhí)行),多線程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多條線程之間調(diào)度(切換),如果CPU調(diào)度線程的時(shí)間足夠快,就造成了多線程并發(fā)執(zhí)行的假象。同樣如果頻繁的切換線程,使得CPU在N個(gè)線程之間調(diào)度,消耗大量的的CPU資源使得線程的執(zhí)行效率降低,多線程開發(fā)我們要找到平衡點(diǎn),控制線程的數(shù)量。
優(yōu)點(diǎn)
提高程序的執(zhí)行效率,不會(huì)因?yàn)橐恍┖臅r(shí)操作影響其他功能正常運(yùn)行同時(shí)還可以提高資源利用率,CPU和內(nèi)存的利用率。
缺點(diǎn)
開啟線程會(huì)占用一定的內(nèi)存空間,如果大量的開啟線程會(huì)占用大量的內(nèi)存空間,同時(shí)CPU在調(diào)度線程上也會(huì)占用資源,在程序設(shè)計(jì)會(huì)變得復(fù)雜,線程的通信和數(shù)據(jù)的共享等等。
iOS的多線程的實(shí)現(xiàn)目前有4種:Pthread, NSThread, GCD, NSOperation,以下我們分別介紹。
Pthread是一套通用的API,多個(gè)平臺(tái)支持,它是基于C語言的,線程的生命周期需要我們手動(dòng)管理,使用難度大,平時(shí)開發(fā)基本不用。我們還是來一個(gè)簡(jiǎn)單的例子
//創(chuàng)建一個(gè)線程并自動(dòng)執(zhí)行 pthread_create(&thread, NULL, start, NULL);NSThread是Objective-C對(duì)C語言Pthread的封裝,面向?qū)ο蠛?jiǎn)單易用,可直接操作線程對(duì)象,同樣生命周期需要我們管理。開發(fā)中不常使用,以下是對(duì)NSThread方法得說明。
創(chuàng)建、啟動(dòng)線程N(yùn)SThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];[thread start];主線程相關(guān)用法+ (NSThread *)mainThread; // 獲得主線程- (BOOL)isMainThread; // 是否為主線程+ (BOOL)isMainThread; // 是否為主線程獲得當(dāng)前線程 NSThread *current = [NSThread currentThread];線程的名字- (void)setName:(NSString *)n;- (NSString *)name;線程的調(diào)度優(yōu)先級(jí),調(diào)度優(yōu)先級(jí)的取值范圍是0.0 ~ 1.0,默認(rèn)0.5,值越大,優(yōu)先級(jí)越高+ (double)threadPRiority;+ (BOOL)setThreadPriority:(double)p;- (double)threadPriority;- (BOOL)setThreadPriority:(double)p;創(chuàng)建線程后自動(dòng)啟動(dòng)線程[NSThread detachNewThreadSelector:@selector(test) toTarget:self withObject:nil];隱式創(chuàng)建并啟動(dòng)線程[self performSelectorInBackground:@selector(test) withObject:nil];控制線程狀態(tài)//啟動(dòng)線程- (void)start; //阻塞(暫停)線程+ (void)sleepUntilDate:(NSDate *)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;//強(qiáng)制停止線程+ (void)exit;//線程停止了,就不能再次開啟任務(wù)-線程間通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;GCD全稱是Grand Central Dispatch,蘋果公司為多核的并行運(yùn)算提出的解決方案,會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核),會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程),雖然是純C語言,基于Block實(shí)現(xiàn),但是使用起來非常方便。想要更深的了解GCD就必須知道任務(wù)和隊(duì)列。
任務(wù):就是需要執(zhí)行的操作,我們可以定制任務(wù),任務(wù)的執(zhí)行分為同步執(zhí)行和異步執(zhí)行,區(qū)別在于是否開啟新的線程。
同步執(zhí)行
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);異步執(zhí)行
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);隊(duì)列:就是用來存放任務(wù),把我們定制的任務(wù)存放在隊(duì)列中,GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出,放到對(duì)應(yīng)的線程中執(zhí)行,任務(wù)的取出遵循隊(duì)列FIFO原則:先進(jìn)先出,后進(jìn)后出。隊(duì)列分為兩大類型:并發(fā)隊(duì)列和串行隊(duì)列。
并發(fā)隊(duì)列 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效,GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列,供整個(gè)應(yīng)用使用,不需要手動(dòng)創(chuàng)建
使用dispatch_get_global_queue函數(shù)獲得全局的并發(fā)隊(duì)列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority, // 隊(duì)列的優(yōu)先級(jí)unsigned long flags); // 此參數(shù)暫時(shí)無用,用0即可dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 獲得全局并發(fā)隊(duì)列/*全局并發(fā)隊(duì)列的優(yōu)先級(jí)#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)*/串行隊(duì)列 讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)),GCD中獲得串行有2種
1.使用dispatch_queue_create函數(shù)創(chuàng)建串行隊(duì)列
dispatch_queue_tdispatch_queue_create(const char *label, // 隊(duì)列名稱 dispatch_queue_attr_t attr); // 隊(duì)列屬性,一般用NULL即可dispatch_queue_t queue = dispatch_queue_create("first", NULL); // 創(chuàng)建dispatch_release(queue); // 非ARC需要釋放手動(dòng)創(chuàng)建的隊(duì)列2.使用主隊(duì)列(跟主線程相關(guān)聯(lián)的隊(duì)列),是GCD自帶的一種特殊的串行隊(duì)列,主隊(duì)列的任務(wù)都會(huì)放在主線程中執(zhí)行,使用dispatch_get_main_queue()獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();我們對(duì)任務(wù)同步異步和隊(duì)列串行并發(fā)的執(zhí)行分析
串行隊(duì)列同步執(zhí)行:不開線程,在原來的線程順序執(zhí)行dispatch_queue_t queue = dispatch_queue_create("cs",DISPATCH_QUEUE_SERIAL);dispatch_sync(queue,^{ //串行隊(duì)列,同步執(zhí)行,不開線程,順序執(zhí)行});串行隊(duì)列異步執(zhí)行:開一條線程,在線程中順序執(zhí)行,效率低占用資源少(相對(duì)比較)dispatch_queue_t queue = dispatch_queue_create("cs",DISPATCH_QUEUE_SERIAL);dispatch_async(queue,^{ //串行隊(duì)列,異步執(zhí)行,開一條線程,順序執(zhí)行});并發(fā)隊(duì)列同步執(zhí)行:不開線程,同步執(zhí)行//并發(fā)隊(duì)列dispatch_queue_t queue = dispatch_queue_create("bs",DISPATHCH_QUEUE_CONCURRENT);dispatch_sync(queue,^{//先執(zhí)行同步任務(wù),不開線程});dispatch_async(queue,^{//等到同步任務(wù)執(zhí)行完成后,開啟線程執(zhí)行當(dāng)前操作});dispatch_async(queue,^{//等到同步任務(wù)執(zhí)行完成后,開啟線程執(zhí)行當(dāng)前操作});并發(fā)隊(duì)列異步執(zhí)行:開第一個(gè)線程,并發(fā)執(zhí)行,效率高占用資源大//并發(fā)隊(duì)列dispatch_queue_t queue = dispatch_queue_create("ba",DISPATHCH_QUEUE_CONCURRENT);dispatch_async(queue,^{//開啟線程執(zhí)行當(dāng)前操作});dispatch_async(queue,^{//開啟線程執(zhí)行當(dāng)前操作});1.調(diào)用NSObject方法
//2秒后調(diào)用[self performSelector:@selector(run) withObject:nil afterDelay:2.0];2.使用GCD函數(shù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后異步執(zhí)行這里的代碼...});使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次
static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{ // 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)});可以管理組內(nèi)的隊(duì)列執(zhí)行情況,方便對(duì)線程管理,比如我們異步執(zhí)行兩個(gè)耗時(shí)的操作,等待完成后回到主線程執(zhí)行操作
dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 執(zhí)行1個(gè)耗時(shí)的異步操作});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 執(zhí)行1個(gè)耗時(shí)的異步操作});dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的兩個(gè)異步操作都執(zhí)行完畢后,回到主線程...});NSOperation:是基于GCD實(shí)現(xiàn)的,比GCD多了更加簡(jiǎn)單實(shí)用的功能,更加面向?qū)ο蟆E浜鲜褂肗SOperation和NSOperationQueue實(shí)現(xiàn)多線程編程,我們開發(fā)中經(jīng)常使用。
提供兩種隊(duì)列:主隊(duì)列和全局并發(fā)隊(duì)列
主隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue mainQueue];全局并發(fā)隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];添加任務(wù)
//1.添加一個(gè)NSOperation的子類- (void); addOperation:(NSOperation *)op[queue addOperation:op];//2.添加一個(gè)Block- (void)addOperationWithBlock:(void (^)(void))block;[queue addOperationWithBlock:^{ //代碼}];NSOperation是個(gè)抽象類,我們使用必須使用它的子類,使用NSOperation子類的方式有3種
1. NSInvocationOperation 對(duì)GCD中全局并發(fā)隊(duì)列的封裝
//1.創(chuàng)建操作NSOperation *op = [[NSInvocationOperation alloc] initWithTatger:self selecter:@Selector(downloadImage:) object:@"Invocation"];//2. 加入隊(duì)列中NSOperationQueue *queue = [[NSOperationQueue alloc]init];//異步執(zhí)行[queue addOperation:op];2. NSBlockOperation對(duì)GCD中全局并發(fā)隊(duì)列的封裝
NSOperationQueue *queue = [[NSOperationQueue alloc]init];//添加任務(wù)NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ //需要執(zhí)行的操作}];//添加到隊(duì)列[queue addOperation:op];3. 自定義子類繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
最大并發(fā)數(shù)就是同時(shí)執(zhí)行的任務(wù)數(shù),可以使用下面的方法
- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;取消
- (void)cancelAllOperations;//也可以調(diào)用NSOperation的- (void)cancel方法取消單個(gè)操作暫停/恢復(fù)
// YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列- (void)setSuspended:(BOOL)b; - (BOOL)isSuspended;設(shè)置NSOperation在queue中的優(yōu)先級(jí),可以改變操作的執(zhí)行優(yōu)先級(jí)
- (NSOperationQueuePriority)queuePriority;- (void)setQueuePriority:(NSOperationQueuePriority)p;/* 優(yōu)先級(jí)的取值 NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8*/可以監(jiān)聽一個(gè)操作的執(zhí)行完畢
- (void (^)(void))completionBlock;- (void)setCompletionBlock:(void (^)(void))block;NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序,比如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B
// 操作B依賴于操作A[operationB addDependency:operationA];我們之前提到的原子性和非原子性,相信在這次的介紹中已經(jīng)有根深的理解了
1.資源共享:一個(gè)資源可能被多個(gè)線程共享,多線程訪問同一塊資源容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全
用來防止兩個(gè)進(jìn)程或線程在同一時(shí)刻訪問相同的共享資源。
@synchronized(鎖對(duì)象) { // 需要鎖定的代碼 }優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題 缺點(diǎn):需要消耗大量的CPU資源
死鎖: 是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程。(在學(xué)校每次考試都考)
今天寫了好長(zhǎng)時(shí)間,總結(jié)整理,眼睛感覺已經(jīng)不是自己的了,發(fā)現(xiàn)有不對(duì)的地方歡迎評(píng)論和聯(lián)系微博
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注