-
進程
- 進程是指在系統(tǒng)中正在運行的一個應(yīng)用程序
-
線程
- 1個進程要想執(zhí)行任務(wù),必須得有線程(每1個進程至少要有1條線程)
- 1個線程中任務(wù)的執(zhí)行是串行的(
執(zhí)行完上一個才能執(zhí)行下一個
)
-
多線程
- 1個進程中可以開啟多條線程猜揪,多條線程可以并行(同時)執(zhí)行不同的任務(wù)
- 線程可以并行, 但是每個線程中的任務(wù)還是串行
-
多線程原理
- 多線程并發(fā)(同時)執(zhí)行惭墓,其實是CPU快速地在多條線程之間調(diào)度(切換)
-
多線程優(yōu)缺點
- 優(yōu)點
- 能適當提高程序的執(zhí)行效率
- 能適當提高資源利用率(CPU、內(nèi)存利用率)
- 缺點
- 線程越多而姐,CPU在調(diào)度線程上的開銷就越大
- 如果開啟大量的線程腊凶,會降低程序的性能
程序設(shè)計更加復雜:比如線程之間的通信、多線程的數(shù)據(jù)共享
- 優(yōu)點
pthread
- 類型:
C語言中類型的結(jié)尾通常 _t/Ref毅人,而且不需要使用 *
/*
參數(shù):
1. 線程代號的地址
2. 線程的屬性
3. 調(diào)用函數(shù)的指針
- void *(*)(void *)
- 返回值 (函數(shù)指針)(參數(shù))
- void * 和 OC 中的 id 是等價的
4. 傳遞給該函數(shù)的參數(shù)
返回值:
如果是0吭狡,表示正確
如果是非0,表示錯誤碼
*/
NSString *str = @"lnj";
pthread_t thid;
int res = pthread_create(&thid, NULL, &demo, (__bridge void *)(str));
if (res == 0) {
NSLog(@"OK");
} else {
NSLog(@"error %d", res);
}
NSThread
- 一個NSThread對象就代表一條線程
- 創(chuàng)建線程的幾種方式
- alloc/init
// 1.創(chuàng)建線程
NJThread *thread = [[NJThread alloc] initWithTarget:self selector:@selector(demo:) object:@"lnj"];
// 設(shè)置線程名稱
[thread setName:@"xmg"];
// 設(shè)置線程的優(yōu)先級
// 優(yōu)先級僅僅說明被CPU調(diào)用的可能性更大
[thread setThreadPriority:1.0];
// 2.啟動線程
[thread start];
- detach/performSelector
- 優(yōu)點:簡單快捷
- 缺點:無法對線程進行更詳細的設(shè)置
// 1.創(chuàng)建線程
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"lnj"];
// 1.創(chuàng)建線程
// 注意: Swift中不能使用, 蘋果認為這個方法不安全
[self performSelectorInBackground:@selector(demo:) withObject:@"lnj"];
- 多線程的安全隱患
- 被鎖定的代碼同一時刻只能有一個線程執(zhí)行
@synchronized(鎖對象) { // 需要鎖定的代碼 }
互斥鎖的優(yōu)缺點
優(yōu)點:能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
缺點:需要消耗大量的CPU資源-
互斥鎖注意點
- 鎖定1份代碼只用1把鎖丈莺,用多把鎖是無效的
- 鎖定范圍越大, 性能越差
-
原子和非原子屬性
- atomic:線程安全划煮,需要消耗大量的資源
- nonatomic:非線程安全,適合內(nèi)存小的移動設(shè)備
-
自旋鎖 & 互斥鎖
- 共同點
都能夠保證同一時間缔俄,只有一條線程執(zhí)行鎖定范圍的代碼 - 不同點
- 互斥鎖:如果發(fā)現(xiàn)有其他線程正在執(zhí)行鎖定的代碼弛秋,線程會進入"休眠"狀態(tài),等待其他線程執(zhí)行完畢俐载,打開鎖之后蟹略,線程會被"喚醒"
- 自旋鎖:如果發(fā)現(xiàn)有其他線程正在執(zhí)行鎖定的代碼,線程會"一直等待"鎖定代碼執(zhí)行完成遏佣!
自旋鎖更適合執(zhí)行非常短的代碼挖炬!
- 共同點
-
線程間通信
- 子線程做耗時操作, 主線程更新數(shù)據(jù)
[self performSelectorInBackground:@selector(download) withObject:nil];
/*
waitUntilDone是否等待被調(diào)用方法執(zhí)行完成,有可能也會等待調(diào)用方法的執(zhí)行完成状婶!
YES: 等待被調(diào)用線程執(zhí)行完畢再執(zhí)行后面的代碼
NO : 不用等待被調(diào)用線程執(zhí)行完畢就可以執(zhí)行后面的代碼
*/
[self performSelectorOnMainThread:@selector(showImage:) withObject:[UIImage imageWithData:data] waitUntilDone:YES];
---
###GCD
- GCD中有2個核心概念
+ 任務(wù):執(zhí)行什么操作
+ 隊列:用來存放任務(wù)
- 執(zhí)行任務(wù)
+ 同步方法: dispatch_sync
+ 異步方法: dispatch_async
+ 同步和異步的區(qū)別
* 同步:只能在當前線程中執(zhí)行任務(wù)意敛,不具備開啟新線程的能力
* 異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
- 隊列
+ 并發(fā)隊列
* 可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
* 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
+ 串行隊列
* 讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后膛虫,再執(zhí)行下一個任務(wù))
- 注意點
+ 同步和異步主要影響:能不能開啟新的線程
* 同步:只是在當前線程中執(zhí)行任務(wù)草姻,不具備開啟新線程的能力
* 異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
+ 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
* 并發(fā):允許多個任務(wù)并發(fā)(同時)執(zhí)行
* 串行:一個任務(wù)執(zhí)行完畢后稍刀,再執(zhí)行下一個任務(wù)
- 各種任務(wù)隊列搭配
+ 同步 + 串行
+ 同步 + 并發(fā)
+ 異步 + 串行
+ 異步 + 并發(fā)
+ 異步 + 主隊列
+ 同步 + 主隊列
- GCD線程間通信
```objc
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行耗時的異步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主線程撩独,執(zhí)行UI刷新操作
});
});
- GCD其它用法
- 延時執(zhí)行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后執(zhí)行這里的代碼...
});
-
一次性代碼
- 使用dispatch_once函數(shù)能保證某段代碼在
程序運行過程中
只被執(zhí)行1次
- 使用dispatch_once函數(shù)能保證某段代碼在
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認是線程安全的)
});
- 快速迭代
```objc
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
// 執(zhí)行10次代碼,index順序不確定
});
-
barrier
- 在前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行账月,而且它后面的任務(wù)等它執(zhí)行完成之后才會執(zhí)行
不能是全局的并發(fā)隊列
所有的任務(wù)都必須在一個隊列中
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
- 隊列組
```objc
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后综膀,回到主線程...
});
NSOperation
-
NSOperation的作用
- 配合使用NSOperation和NSOperationQueue也能實現(xiàn)多線程編程
-
NSOperation和NSOperationQueue實現(xiàn)多線程的具體步驟
- 先將需要執(zhí)行的操作封裝到一個NSOperation對象中
- 然后將NSOperation對象添加到NSOperationQueue中
- 系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來
- 將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行
NSOperation的子類
NSOperation是個抽象類,并不具備封裝操作的能力捶障,必須使用它的子類
使用NSOperation子類的方式有3種
1.NSInvocationOperation
2.NSBlockOperation
3.自定義子類繼承NSOperation僧须,實現(xiàn)內(nèi)部相應(yīng)的方法
- 1.NSInvocationOperation
1.創(chuàng)建NSInvocationOperation對象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
2.調(diào)用start方法開始執(zhí)行操作
- (void)start;
一旦執(zhí)行操作,就會調(diào)用target的sel方法
注意
- 默認情況下项炼,調(diào)用了start方法后并不會開一條新線程去執(zhí)行操作担平,而是在當前線程同步執(zhí)行操作
- 只有將NSOperation放到一個NSOperationQueue中示绊,才會異步執(zhí)行操作
- 2.NSBlockOperation
1.創(chuàng)建NSBlockOperation對象
+ (id)blockOperationWithBlock:(void (^)(void))block;
通過addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
注意
- 只要NSBlockOperation封裝的操作數(shù) > 1,就會異步執(zhí)行操作
NSOperationQueue
-
NSOperationQueue的作用
- NSOperation可以調(diào)用start方法來執(zhí)行任務(wù)暂论,但默認是同步執(zhí)行的
- 如果將NSOperation添加到NSOperationQueue(操作隊列)中面褐,系統(tǒng)會自動異步執(zhí)行NSOperation中的操作
添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
最大并發(fā)數(shù)
-
什么是并發(fā)數(shù)
- 同時執(zhí)行的任務(wù)數(shù)
比如,同時開3個線程執(zhí)行3個任務(wù)取胎,并發(fā)數(shù)就是3
最大并發(fā)數(shù)的相關(guān)方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
隊列的取消展哭、暫停、恢復
-
取消隊列的所有操作
- (void)cancelAllOperations;
提示
:也可以調(diào)用NSOperation的- (void)cancel方法取消單個操作 暫停和恢復隊列
- (void)setSuspended:(BOOL)b; // YES代表暫停隊列闻蛀,NO代表恢復隊列
- (BOOL)isSuspended;
操作依賴
- NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序
比如一定要讓操作A執(zhí)行完后匪傍,才能執(zhí)行操作B,可以這么寫
[operationB addDependency:operationA]; // 操作B依賴于操作A
可以在不同queue的NSOperation之間創(chuàng)建依賴關(guān)系
操作的監(jiān)聽
- 可以監(jiān)聽一個操作的執(zhí)行完畢
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
自定義NSOperation
- 自定義NSOperation的步驟
重寫- (void)main方法觉痛,在里面實現(xiàn)想執(zhí)行的任務(wù)
-
重寫- (void)main方法的注意點:
- 自己創(chuàng)建自動釋放池(因為如果是異步操作役衡,無法訪問主線程的自動釋放池)
- 經(jīng)常通過- (BOOL)isCancelled方法檢測操作是否被取消,對取消做出響應(yīng)
NSOperationQueue和GCD對比
+ GCD
* 并發(fā): 自己創(chuàng)建, 全局
* 串行: 自己創(chuàng)建, 主隊列
+ NSOperationQueue
* 主隊列: mainQueue
+ 永遠在主線程中執(zhí)行
* 自己創(chuàng)建隊列: alloc init
+ 會開啟新的線程, 在子線程中執(zhí)行
+ 如何控制并行和串行
* maxConcurrentOperationCount = -1 ; 并行
* 默認就是并行
* maxConcurrentOperationCount = 1 ; 串行
* maxConcurrentOperationCount = 0 ; 不會執(zhí)行
+ 使用步驟:
* 和GCD一樣
* 1.創(chuàng)建操作(任務(wù))
* 2.將任務(wù)添加到隊列中
+ 快速添加任務(wù)的方法
// 只要利用隊列調(diào)用addOperationWithBlock:方法, 系統(tǒng)內(nèi)部會自動封裝成一個NSBlockOperation \
然后再添加到隊列中
[queue addOperationWithBlock:^{
NSLog(@"3 == %@", [NSThread currentThread]);
}];
- 隊列的暫停和恢復以及取消
- 暫停
- self.queue.suspended = YES;
- 注意點:暫停其實是暫停下一個任務(wù), 而不能暫停當前任務(wù)
- 恢復
- self.queue.suspended = NO;
- 注意點: 恢復之后會繼續(xù)執(zhí)行隊列中沒有被執(zhí)行的操作
- 取消
- [self.queue cancelAllOperations];
- 實現(xiàn)原理: 調(diào)用所有操作的cancel方法
- 注意點: 取消其實是取消下一個任務(wù), 而不能取消當前任務(wù)
- 如果自定義操作中做了很多耗時操作, 蘋果建議定期檢查是否已經(jīng)取消了
- 暫停
- (void)main
{
// 耗時操作1
for (int i = 0; i < 10000; i++) { // 500
NSLog(@"%i ==== %@", i, [NSThread currentThread]);
}
NSLog(@"++++++++++++++++++++++++++++++++++++++");
if (self.isCancelled) {
return;
}
// 耗時操作2
for (int i = 0; i < 10000; i++) { // 500
NSLog(@"%i ==== %@", i, [NSThread currentThread]);
}
if (self.isCancelled) {
return;
}
NSLog(@"++++++++++++++++++++++++++++++++++++++");
// 好所操作3
for (int i = 0; i < 10000; i++) { // 500
NSLog(@"%i ==== %@", i, [NSThread currentThread]);
}
}
- 隊列之間的依賴
- 在操作添加到隊列之前, 利用操作調(diào)用addDependency, 就快要添加依賴
- 添加依賴之后, 只有所有依賴的任務(wù)都執(zhí)行完畢, 才會執(zhí)行當前任務(wù)
- 注意點: 不要相互依賴
- 特點: 跨隊列依賴(GCD默認是不支持)
// 3.添加依賴
[op5 addDependency:op1];
[op5 addDependency:op2];
[op5 addDependency:op3];
[op5 addDependency:op4];
-
操作的監(jiān)聽
- 只需要利用操作調(diào)用completionBlock即可
- 只要任務(wù)執(zhí)行完畢, 就會回調(diào)completionBlock
-
線程間的通信
- 將任務(wù)添加到自己創(chuàng)建的隊列中
- 再利用mainQueue回到主隊列