多線程的基本知識
先補(bǔ)一發(fā)基礎(chǔ)知識
什么是線程
線程,有時(shí)被稱為輕量級進(jìn)程(Lightweight Process,LWP),是程序執(zhí)行流的最小單元。一個(gè)標(biāo)準(zhǔn)的線程由線程ID,當(dāng)前指令指針(PC),寄存器集合和堆棧組成。
線程是程序中一個(gè)單一的順序控制流程。進(jìn)程內(nèi)一個(gè)相對獨(dú)立的、可調(diào)度的執(zhí)行單元,是系統(tǒng)獨(dú)立調(diào)度和分派CPU的基本單位指運(yùn)行中的程序的調(diào)度單位。在單個(gè)程序中同時(shí)運(yùn)行多個(gè)線程完成不同的工作,稱為多線程。
線程與進(jìn)程的關(guān)系
線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)兒在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。一個(gè)線程可以創(chuàng)建和撤消另一個(gè)線程,同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。
線程與進(jìn)程的區(qū)別
進(jìn)程
進(jìn)程是資源分配的基本單位。每個(gè)進(jìn)程擁有自己獨(dú)立的進(jìn)程控制塊(PCB,Process Control Block),不同的進(jìn)程擁有不同的虛擬地址空間。
進(jìn)程間通信,常用方法為:
- 管道(pipe)
- 消息隊(duì)列(message queue)
- 信號(sinal)
- 信號量(semophore)
- 共用內(nèi)存(shared memory)
- 套接字(socket)
線程
線程是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位。線程只由相關(guān)堆棧(系統(tǒng)?;蛴脩魲#┘拇嫫骱途€程控制表(TCB,Thread Control Table)組成,同一進(jìn)程內(nèi)的不同線程共享同一地址空間。
線程間通信,常用方法為:
- 鎖(lock):互斥鎖、條件變量、讀寫鎖等
- 信號量(semophore)
- 信號(signal):用于線程同步
線程的狀態(tài)

iOS中的多線程技術(shù)
pthread
簡介
POSIX線程(英語:POSIX Threads,常被縮寫為Pthreads)是POSIX的線程標(biāo)準(zhǔn),定義了創(chuàng)建和操縱線程的一套API。
實(shí)現(xiàn)POSIX 線程標(biāo)準(zhǔn)的庫常被稱作Pthreads,一般用于Unix-like POSIX 系統(tǒng),如Linux、Solaris。但是Microsoft Windows上的實(shí)現(xiàn)也存在,例如直接使用Windows API實(shí)現(xiàn)的第三方庫pthreads-w32;而利用Windows的SFU/SUA子系統(tǒng),則可以使用微軟提供的一部分原生POSIX API。
使用
包含頭文件
#import <pthread.h>-
創(chuàng)建線程,并且開啟線程執(zhí)行任務(wù)
// 創(chuàng)建線程——定義一個(gè)pthread_t類型變量 pthread_t thread; // 開啟線程——執(zhí)行任務(wù) pthread_create(&thread, NULL, run, NULL); // 新線程調(diào)用方法,里邊為需要執(zhí)行的任務(wù) void * run(void *param) { NSLog(@"%@", [NSThread currentThread]); return NULL; }
NSThread
簡介
NSThread是蘋果官方提供的,基于c語言封裝,使用起來比pthread更加面向?qū)ο?,簡單易用,可以直接操作線程對象。不過也需要需要我們自己管理線程的生命周期(主要是創(chuàng)建),我們在開發(fā)的過程中偶爾使用NSThread。比如我們會(huì)經(jīng)常調(diào)用[NSThread currentThread]來顯示當(dāng)前的進(jìn)程信息。
使用
啟動(dòng)
-
創(chuàng)建線程,并手動(dòng)啟動(dòng)
// 創(chuàng)建 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil]; // 啟動(dòng) [thread start];
-
創(chuàng)建線程并啟動(dòng)
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil]; -
使用NSObject的方法創(chuàng)建并啟動(dòng)
[self performSelectorInBackground:@selector(run:) withObject:nil];
其他方法
//取消線程
- (void)cancel;
//啟動(dòng)線程
- (void)start;
//強(qiáng)制停止線程
+ (void)exit;
//判斷某個(gè)線程的狀態(tài)的屬性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//設(shè)置和獲取線程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//獲取當(dāng)前線程信息
+ (NSThread *)currentThread;
//獲取主線程信息
+ (NSThread *)mainThread;
//使當(dāng)前線程暫停(阻塞)一段時(shí)間,或者暫停到某個(gè)時(shí)刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
GCD
簡介
Grand Central Dispatch (GCD) 是Apple開發(fā)的一個(gè)多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。
使用
啟動(dòng)
-
創(chuàng)建隊(duì)列
使用
dispatch_queue_create來創(chuàng)建隊(duì)列- 第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識符,用于DEBUG,可為空
- 第二個(gè)參數(shù)用來識別是串行隊(duì)列還是并發(fā)隊(duì)列。
DISPATCH_QUEUE_SERIAL表示串行隊(duì)列,DISPATCH_QUEUE_CONCURRENT表示并發(fā)隊(duì)列。- 這里的
同步(sync)和異步(async)的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程,直到Block中的任務(wù)執(zhí)行完畢
- 這里的
使用dispatch_get_xxx來獲取隊(duì)列
dispatch_get_global_queue會(huì)獲取一個(gè)全局隊(duì)列-
dispatch_get_main_queue會(huì)獲取主隊(duì)列,也就是UI隊(duì)列// 串行隊(duì)列的創(chuàng)建方法 dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL); // 并發(fā)隊(duì)列的創(chuàng)建方法 dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
-
創(chuàng)建任務(wù)
// 同步執(zhí)行任務(wù)創(chuàng)建方法 dispatch_sync(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 這里放任務(wù)代碼 }); // 異步執(zhí)行任務(wù)創(chuàng)建方法 dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 這里放任務(wù)代碼 });
管理
-
柵欄方法
dispatch_barrier_async第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來,當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到
dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄。dispatch_async(queue, ^{...}); dispatch_async(queue, ^{...}); // 當(dāng)上面兩個(gè)異步任務(wù)執(zhí)行完后才執(zhí)行下面的異步任務(wù) dispatch_barrier_async(queue, ^{...}); dispatch_async(queue, ^{...}); dispatch_async(queue, ^{...}); -
延時(shí)執(zhí)行方法
dispatch_after當(dāng)我們需要延遲執(zhí)行一段代碼時(shí),就需要用到GCD的
dispatch_after方法。dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后異步執(zhí)行這里的代碼 // ... }); -
一次性代碼(只執(zhí)行一次)
dispatch_once在創(chuàng)建單例、或者有整個(gè)程序運(yùn)行過程中只執(zhí)行一次的代碼時(shí),我們就用到了GCD的
dispatch_once方法。使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次。static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的) }); -
快速迭代方法
dispatch_applyGCD給我們提供了快速迭代的方法
dispatch_apply,使我們可以同時(shí)遍歷。比如說遍歷0~5這6個(gè)數(shù)字,for循環(huán)的做法是每次取出一個(gè)元素,逐個(gè)遍歷。dispatch_apply可以同時(shí)遍歷多個(gè)數(shù)字。dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(6, queue, ^(size_t index) { NSLog(@"%zd------%@",index, [NSThread currentThread]); }); -
隊(duì)列組
dispatch_group分別異步執(zhí)行2個(gè)耗時(shí)操作,然后當(dāng)2個(gè)耗時(shí)操作都執(zhí)行完畢后再回到主線程執(zhí)行操作。這時(shí)候我們可以用到GCD的隊(duì)列組。
- 我們可以先把任務(wù)放到隊(duì)列中,然后將隊(duì)列放入隊(duì)列組中。
- 調(diào)用隊(duì)列組的
dispatch_group_notify回到主線程執(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(), ^{ // 等前面的異步操作都執(zhí)行完畢后,回到主線程... });
NSOperation
簡介
NSOperation是蘋果提供給我們的一套多線程解決方案。實(shí)際上NSOperation是基于GCD更高一層的封裝,但是比GCD更簡單易用、代碼可讀性也更高。
使用
啟動(dòng)
-
創(chuàng)建任務(wù)
不使用
NSOperationQueue,單獨(dú)使用NSOperation的情況下,系統(tǒng)同步執(zhí)行操作-
使用子類
NSInvocationOperationNSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; [op start]; - (void)run { // do sth } -
使用子類
NSBlockOperationNSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ // do sth }]; // 添加額外的任務(wù)(在子線程執(zhí)行) [op addExecutionBlock:^{ // do sth }]; [op addExecutionBlock:^{ // do sth }]; [op addExecutionBlock:^{ // do sth }]; [op start]; -
定義繼承自
NSOperation的子類,通過實(shí)現(xiàn)內(nèi)部相應(yīng)的方法來封裝任務(wù)- 定義一個(gè)繼承自
NSOperation的子類,重寫main方法
- 定義一個(gè)繼承自
-
-
創(chuàng)建隊(duì)列
主隊(duì)列
-
凡是添加到主隊(duì)列中的任務(wù)(
NSOperation),都會(huì)放到主線程中執(zhí)行NSOperationQueue *queue = [NSOperationQueue mainQueue];
-
其他隊(duì)列(非主隊(duì)列)
添加到這種隊(duì)列中的任務(wù)(
NSOperation),就會(huì)自動(dòng)放到子線程中執(zhí)行-
同時(shí)包含了:串行、并發(fā)功能
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
-
把任務(wù)加入隊(duì)列
-
(void)addOperation:(NSOperation *)operation;// 1.創(chuàng)建隊(duì)列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2. 創(chuàng)建操作 // 創(chuàng)建NSInvocationOperation NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; // 創(chuàng)建NSBlockOperation NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ // do sth }]; // 3. 添加操作到隊(duì)列中:addOperation: [queue addOperation:op1]; // [op1 start] [queue addOperation:op2]; // [op2 start] -
(void)addOperationWithBlock:(void (^)(void))block;// 1. 創(chuàng)建隊(duì)列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2. 添加操作到隊(duì)列中:addOperationWithBlock: [queue addOperationWithBlock:^{ for (int i = 0; i < 2; ++i) { NSLog(@"-----%@", [NSThread currentThread]); } }];
-
管理
-
控制并發(fā)和串行
通過
NSOperationQueue最大并發(fā)數(shù):maxConcurrentOperationCount,實(shí)現(xiàn)串行和并發(fā)的控制-
maxConcurrentOperationCount默認(rèn)情況下為-1,表示不進(jìn)行限制,默認(rèn)為并發(fā)執(zhí)行。 - 當(dāng)
maxConcurrentOperationCount為1時(shí),進(jìn)行串行執(zhí)行。 - 當(dāng)
maxConcurrentOperationCount大于1時(shí),進(jìn)行并發(fā)執(zhí)行,當(dāng)然這個(gè)值不應(yīng)超過系統(tǒng)限制,即使自己設(shè)置一個(gè)很大的值,系統(tǒng)也會(huì)自動(dòng)調(diào)整。
// 創(chuàng)建隊(duì)列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 設(shè)置最大并發(fā)操作數(shù) // queue.maxConcurrentOperationCount = 2; queue.maxConcurrentOperationCount = 1; // 就變成了串行隊(duì)列 -
-
操作依賴
NSOperation和NSOperationQueue最吸引人的地方是它能添加操作之間的依賴關(guān)系。比如說有A、B兩個(gè)操作,其中A執(zhí)行完操作,B才能執(zhí)行操作,那么就需要讓B依賴于A。NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1-----%@", [NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2-----%@", [NSThread currentThread]); }]; [op2 addDependency:op1]; // 讓op2 依賴于 op1,則先執(zhí)行op1,在執(zhí)行op2 [queue addOperation:op1]; [queue addOperation:op2]; -
其他方法
// 提供的方法,可取消單個(gè)操作 - (void)cancel; NSOperation // NSOperationQueue提供的方法,可以取消隊(duì)列的所有操作 - (void)cancelAllOperations; // 可設(shè)置任務(wù)的暫停和恢復(fù),YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列 - (void)setSuspended:(BOOL)bool; // 判斷暫停狀態(tài) - (BOOL)isSuspended;- 這里的暫停和取消并不代表可以將當(dāng)前的操作立即取消,而是當(dāng)當(dāng)前的操作執(zhí)行完畢之后不再執(zhí)行新的操作。
- 暫停和取消的區(qū)別就在于:暫停操作之后還可以恢復(fù)操作,繼續(xù)向下執(zhí)行;而取消操作之后,所有的操作就清空了,無法再接著執(zhí)行剩下的操作。
對比
- pthread
- 優(yōu)點(diǎn)
- 接近底層
- 缺點(diǎn)
- 難于操作,需要自己管理線程的生命周期
- 優(yōu)點(diǎn)
- NSThread
- 優(yōu)點(diǎn)
- 比
pthread更加的面向?qū)ο?/li> - 輕量級最低,相對簡單
- 比
- 缺點(diǎn)
- 需要自己管理線程的生命周期,如生命周期、線程同步、睡眠等
- 優(yōu)點(diǎn)
- GCD
- 優(yōu)點(diǎn)
- 可用于多核的并行運(yùn)算
- 自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)
- 自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- 只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
- 缺點(diǎn)
- 不可取消
- 優(yōu)點(diǎn)
- NSOperation
- 優(yōu)點(diǎn)
- 比GCD簡單易用,代碼可讀性更高
- 自帶線程周期管理,操作上可更注重自己邏輯
- 能添加操作之間的依賴關(guān)系
- 可以指定最大并發(fā)數(shù)
- 可取消
- 缺點(diǎn)
- 沒有GCD簡潔
- 優(yōu)點(diǎn)