版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
本文鏈接:https://yotrolz.com/posts/c8aae65b/
一、pthread(很少使用)
簡介:
- 一套通用的多線程API
- 使用與Unix、Linux、Window等系統(tǒng)
- 跨平臺、可移植
- 使用難度大
特點:
- C語言
- 程序員管理線程生命周期
使用方法:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
pthread_t thread;
NSLog(@"%@", [NSThread currentThread]); // 主線程中執(zhí)行
pthread_create(&thread, NULL, run, NULL); // 在子線程中執(zhí)行演示的操作
}
/** 延時操作 */
void * run(void *param) {
// 耗時操作
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"%zd", i);
}
NSLog(@"%@", [NSThread currentThread]); // 子線程中執(zhí)行
return NULL;
}
二、NSTread(偶爾使用)
簡介:
- 使用更加面向?qū)ο?/li>
- 使用簡單,可直接操作線程對象
特點:
- OC語言
- 程序員管理線程生命周期
線程狀態(tài):
線程狀態(tài).png
常用方法:
-
創(chuàng)建子線程-方式一
// 創(chuàng)建一個子線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];
// 設(shè)置創(chuàng)建的線程的名字
[thread setName:@"sonThread"];
// 開啟這個線程
[thread start];
```
-
創(chuàng)建子線程-方式二
// 方式二: 創(chuàng)建后無需手動開啟,系統(tǒng)會自動開啟
[NSThread detachNewThreadSelector:@selector(test) toTarget:self withObject:nil];
```
-
創(chuàng)建子線程-方式三
// 方式三:隱式創(chuàng)建并自定開啟
[self performSelectorInBackground:@selector(test) withObject:nil];
```
-
獲得當(dāng)前線程
NSThread *current = [NSThread currentThread];
-
線程的名字
// 設(shè)置線程的名字 - (void)setName:(NSString *)name; // 獲取線程的名字 - (NSString *)name;
-
啟動線程
- (void)start; // 進(jìn)入就緒狀態(tài) -> 運行狀態(tài)。當(dāng)線程任務(wù)執(zhí)行完畢,自動進(jìn)入死亡狀態(tài)
-
阻塞線程
+ (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; // 進(jìn)入阻塞狀態(tài)
-
強制殺死線程
+ (void)exit; // 進(jìn)入死亡狀態(tài)
- 注意:一旦線程
死亡
(停止),就不能再次開啟
- 注意:一旦線程
線程安全
-
線程安全隱患
線程安全隱患.png -
解決方式:
互斥鎖
線程安全解決方式.png 互斥鎖的使用前提:
多條線程
搶奪同一塊資源
-
互斥鎖的優(yōu)缺點:
- 優(yōu)點:能有效防止因多線程搶奪資源造成的
數(shù)據(jù)安全
問題 - 缺點:需要消耗大量的
CPU資源
- 優(yōu)點:能有效防止因多線程搶奪資源造成的
相關(guān)專業(yè)術(shù)語:
線程同步
線程通信
- 在1個線程中執(zhí)行完特定任務(wù)后,轉(zhuǎn)到另1個線程繼續(xù)執(zhí)行任務(wù)
- 線程通信常用方法
// 在主線程執(zhí)行SEL
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// 在指定的thread執(zhí)行SEL
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait;
三、GCD(經(jīng)常使用)
- 簡介:
- 全稱是
Grand Central Dispatch
,可譯為“牛逼的中樞調(diào)度器” - 旨在替代NSTread等線程技術(shù)
- 充分利用設(shè)備的
多核
- 全稱是
- 特點:
- C語言
- 系統(tǒng)
自動管理
線程生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- GCD兩個重要概念:
任務(wù)
和隊列
- 任務(wù):執(zhí)行什么
操作
(任務(wù)) - 隊列:用來
存放任務(wù)
- 任務(wù):執(zhí)行什么
- GCD的使用步驟:
- 1.
定制
任務(wù) - 2.將任務(wù)
添加
到隊列中 - 注意:任務(wù)的取出(執(zhí)行),按照隊列的
FIFO
原則(先進(jìn)先出,后進(jìn)后出
)
- 1.
- GCD有四個用來
執(zhí)行任務(wù)
的常用函數(shù)- 同步執(zhí)行:只能在
當(dāng)前線程
中執(zhí)行任務(wù),不具備開啟新線程的能力
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- 異步執(zhí)行:
可以(但不一定)
在新的線程中執(zhí)行任務(wù),具備開啟新線程
的能力
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
- 同步執(zhí)行:只能在
dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
```
- 隊列的類型:(Serial Dispatch Queue和Concurrent Dispatch Queue)
- 1.串行隊列:讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù))
- 2.并發(fā)隊列:可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
- `注意`:并發(fā)隊列的并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
- 同步、異步、并發(fā)、串行
- 同步和異步主要影響:`能不能開啟新的線程`
- 同步:只是在`當(dāng)前線程`中執(zhí)行任務(wù),不具備開啟新線程的能力
- 異步:`可以(但不是一定)`在`新的線程`中執(zhí)行任務(wù),具備開啟新線程的能力
- 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
- 并發(fā):允許多個任務(wù)并發(fā)(同時)執(zhí)行
- 串行:一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù)
- 隊列的創(chuàng)建
- 并發(fā)隊列的創(chuàng)建:
```objc
// 創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
```
- GCD默認(rèn)已經(jīng)提供了`全局的并發(fā)隊列`,供整個應(yīng)用使用,可以無需手動創(chuàng)建,我們只要獲取就可以使用
```objc
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority, // 隊列的優(yōu)先級
unsigned long flags); // 此參數(shù)暫時無用,用0即可(官方文檔)
// 獲得全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 全局并發(fā)隊列的優(yōu)先級
#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 // 后臺
```
- 串行隊列的創(chuàng)建:
- 1.創(chuàng)建一個串行隊列:
```objc
dispatch_queue_t queue = dispatch_queue_create("com.YotrolZ", DISPATCH_QUEUE_SERIAL);
```
- 注意:隊列類型傳入`NULL`也可以;
- 2.獲取系統(tǒng)自帶的一種特殊的串行隊列-`主隊列`
```objc
dispatch_queue_t queue = dispatch_get_main_queue()
```
- 主隊列的特點:放在`主隊列`中的任務(wù),都是在`主線程`中執(zhí)行
- 注意點:
- 在串行隊列中使用同步函數(shù)添加任務(wù)時會出現(xiàn)卡主當(dāng)前串行隊列的現(xiàn)象!!
- 解釋:在串行隊列中,任務(wù)是一個一個按順序執(zhí)行了,當(dāng)使用同步函數(shù)往隊列中添加任務(wù)時理論上要立即執(zhí)行改任務(wù),但由于串行隊列還沒有執(zhí)行完畢,理論上又輪不到剛添加的任務(wù)執(zhí)行,會出現(xiàn)你等我,我等你的現(xiàn)象
- GCD的其他使用:
- 1.延時執(zhí)行
```objc
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 這里協(xié)商2秒后要執(zhí)行的代碼
});
```
- 2.一次性代碼(讓某段代碼在整個程序運行過程中`只運行一次`)
- 一次性代碼函數(shù)是`線程安全`的
```objc
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 這里寫上只執(zhí)行1次的代碼
});
```
- 3.快速迭代
- 注意點:index`順序不確定`
```objc
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
// 執(zhí)行10次代碼
});
```
- 4.隊列組
```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í)行完畢后,回到主線程執(zhí)行此操作
});
```
### 四、NSOperation+NSOperationQueue(經(jīng)常使用)
- 簡介:
- NSOperation基于GCD(底層是GCD),性能肯定也就沒有GCD高了
- 比GCD多了一些更簡單的功能
- 使用更面向?qū)ο?- 特點:
- OC語言
- 系統(tǒng)`自動管理`線程生命周期
#### NSOperation的使用
- NSOperation是一個`抽象類`,并不具備封裝任務(wù)的功能,我們應(yīng)該使用其子類(3種)
- `NSBlockOperation`
- NSBlockOperation使用方法:
```objc
NSBlockOperation *blockOP = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOperation1--%@", [NSThread currentThread]); // 在主線程執(zhí)行
}];
[blockOP addExecutionBlock:^{
NSLog(@"blockOperation2--%@", [NSThread currentThread]); // 在子線程執(zhí)行(自動開啟線程)
}];
[blockOP addExecutionBlock:^{
NSLog(@"blockOperation3--%@", [NSThread currentThread]); // 又會開啟一個新的子線程
}];
[blockOP start];
```
- NSBlockOperation使用細(xì)節(jié):
- 1.單獨使用blockOperation且`任務(wù)只有一個`時,不會開啟新的線程,在`當(dāng)前線程`中執(zhí)行;
- 2.為blockOperation`再次添加新的任務(wù)`(任務(wù)數(shù)大于1)會開啟新的線程,在`子線程`中執(zhí)行;
- `NSBlockOperation`
- NSInvocationOperation使用方法:
```objc
-(void)invocationOperation {
// 創(chuàng)建任務(wù)
NSInvocationOperation *invocationOP = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 開始任務(wù)
[invocationOP start];
}
-(void)run {
NSLog(@"invocationOperation--%@", [NSThread currentThread]);
}
```
- NSInvocationOperation使用細(xì)節(jié):
單獨使用operation不會開啟新的線程,在`當(dāng)前線程`中執(zhí)行
- `自定義Operation`
- 步驟1.定義一個子類繼承`NSOperation`;
- 步驟2.實現(xiàn)其`- (void)main;`方法,在main方法中添加要執(zhí)行的任務(wù)操作;
```objc
- (void)main { // 添加要執(zhí)行的任務(wù)操作
if (self.isCancelled) return;
for (NSUInteger i = 0; i < 1000; i++) {
NSLog(@"%zd--%@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSUInteger i = 0; i < 1000; i++) {
NSLog(@"%zd--%@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSUInteger i = 0; i < 1000; i++) {
NSLog(@"%zd--%@", i, [NSThread currentThread]);
}
}
```
- 自定義Operation使用細(xì)節(jié):
- 重寫Operation的mian方法時,官方建議我們及時的判斷`isCancelled`,以能夠及時的取消一些耗時的操作;
- NSOperation的其他使用
- 操作的`依賴`和`監(jiān)聽`
```objc
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創(chuàng)建5個任務(wù)操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op1--%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op2--%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op3--%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op4--%@", [NSThread currentThread]);
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op5--%@", [NSThread currentThread]);
}];
// 操作的監(jiān)聽
op4.completionBlock = ^{
// 也是在子線程中執(zhí)行,但是不一定和器任務(wù)操作在一個線程
NSLog(@"op4完成了--%@", [NSThread currentThread]);
};
// 設(shè)置依賴(可以設(shè)置多個,但是不能相互依賴)
// 這樣就保證了op3在op1和op4完成后才執(zhí)行
[op3 addDependency:op1];
[op3 addDependency:op4];
// 將任務(wù)操作添加到隊列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
```
#### NSOperationQueue的使用
- NSOperationQueue的作用
- NSOperation可以調(diào)用start方法來執(zhí)行任務(wù),但默認(rèn)是同步執(zhí)行的,不一定會開啟新的線程
- 將NSOperation添加到NSOperationQueue(操作隊列)中,系統(tǒng)會`自動異步執(zhí)行`NSOperation中的操作
- 將任務(wù)(NSOperation)添加到操作隊列(NSOperationQueue0中
- 方式一:需先創(chuàng)建NSOperation,再添加到NSOperationQUeue中
```objc
-(void)addOperation:(NSOperation *)op;
```
- 方式二:不需先創(chuàng)建NSOperation,直接在添加到NSOperationQueue的時候在block中寫上任務(wù)代碼
```objc
-(void)addOperationWithBlock:(void (^)(void))block;
```
```objc
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 任務(wù)1
[queue addOperationWithBlock:^{
NSLog(@"11--%@", [NSThread currentThread]);
for (NSUInteger i = 0; i < 10; i++) {
NSLog(@"%zd", i);
[NSThread sleepForTimeInterval:1];
}
}];
// 任務(wù)2
[queue addOperationWithBlock:^{
NSLog(@"22--%@", [NSThread currentThread]);
for (NSUInteger i = 0; i < 10; i++) {
NSLog(@"%zd", i);
}
}];
// 任務(wù)3
[queue addOperationWithBlock:^{
NSLog(@"33--%@", [NSThread currentThread]);
for (NSUInteger i = 0; i < 10; i++) {
NSLog(@"%zd", i);
}
}];
```
- 備注:添加到NSOperationQueue中的任務(wù)系統(tǒng)會`自動異步執(zhí)行`,不用start方法
- NSOperationQueue的常見屬性:
- 1.最大并發(fā)數(shù):同時執(zhí)行的任務(wù)數(shù)
```objc
@property NSInteger maxConcurrentOperationCount;
```
- 備注:設(shè)置`NSOperationQueue`的`maxConcurrentOperationCount`為`1`時,也就是同時執(zhí)行一個任務(wù),(`同步執(zhí)行`)
- 2.暫停(掛起狀態(tài))
```objc
@property (getter=isSuspended) BOOL suspended;
```
- 備注:設(shè)置`NSOperationQueue`的`suspended`為`YES`時,會暫停(掛起)隊列中的任務(wù),但是如果某個任務(wù)已經(jīng)在執(zhí)行了,并不會立即暫停這個任務(wù),而是等待這個任務(wù)執(zhí)行完畢后,暫停后面的其他任務(wù);
- 使用場合:暫停(掛起)這個功能,可以用在(假如正在下載一組圖片,用戶拖拽了界面控件等,為了保證流暢的用戶體驗,可以先掛起,等用戶拖拽結(jié)束,再恢復(fù))的情況下使用
- NSOperationQueue取消所有任務(wù):
```objc
-(void)cancelAllOperations;
```
- 備注:NSOperationQueue對象調(diào)用`cancelAllOperations`方法時,會`取消隊列中的所有任務(wù)`,但是如果某個任務(wù)已經(jīng)在執(zhí)行了,并不會立即取消所有任務(wù),而是等待這個任務(wù)執(zhí)行完畢后,取消所有任務(wù);