在iOS
開發(fā)中需要將一些耗時操作放到子線程中去執(zhí)行, 防止阻塞主線程造成卡頓現(xiàn)象, 這時就用到了多線程.
本篇文章主要講解iOS
中多線程的方案斑鸦、GCD
的使用颂鸿、NSOperation
的使用败晴、死鎖現(xiàn)象以及死鎖的本質、多線程的同步方案、文件的多讀單寫操作等.
一. 概念
在iOS
開發(fā)中, 最常用的兩種多線程方案就是GCD
和NSOperation
了.
多線程方案 | 簡介 | 語言 | 生命周期 |
---|---|---|---|
GCD | 充分利用系統(tǒng)的多核 | C | 自動管理 |
NSOperation | 對GCD的封裝, 更加面向對象, 增加了一下實用功能 | OC | 自動管理 |
在理解多線程之前需要對以下概念進行了解
- 進程: 簡單理解為正在運行的一個App就是一個進程.
- 線程: 線程用來執(zhí)行任務, 一個進程中可以有多個線程.
- 同步執(zhí)行: 阻塞當前線程, 在當前線程中執(zhí)行任務, 不具備開啟線程的能力.
- 異步執(zhí)行: 不阻塞當前線程, 具備開啟線程的能力(不一定不開啟新線程)
- 串行隊列: 從隊列中取出任務的順序是FIFO, 上一個任務執(zhí)行完成之后再執(zhí)行下一個任務
- 并發(fā)隊列: 從隊列中取出任務的順序是FIFO, 多個任務同時執(zhí)行, 任務完成的順序不確定(個人理解為, 從隊列中取任務分發(fā)到線程去執(zhí)行)
并發(fā)隊列 | 串行隊列 | 主隊列 | |
---|---|---|---|
同步執(zhí)行(sync) | 沒有開啟線程, 串行執(zhí)行任務 | 沒有開啟線程, 串行執(zhí)行任務 | 沒有開啟線程, 串行執(zhí)行任務 |
異步執(zhí)行(async) | 有開啟線程, 并發(fā)執(zhí)行任務 | 有開啟線程, 串行執(zhí)行任務 | 沒有開啟線程, 串行執(zhí)行任務 |
二. GCD
GCD
中的隊列有四種:
// 串行隊列
dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊列
dispatch_queue_t queue2 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_CONCURRENT);
// 主隊列
dispatch_queue_t queue3 = dispatch_get_main_queue();
// 全局并發(fā)隊列
dispatch_queue_t queue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
在使用dispatch_queue_create
創(chuàng)建隊列時, 第一個參數(shù)是隊列的字符串標簽(隊列的唯一標識), 格式為com.example.myqueue
, 參數(shù)可選, 默認為NULL
. 在iOS4.3
以后第二個參數(shù)傳入DISPATCH_QUEUE_SERIAL
用于創(chuàng)建串行隊列, DISPATCH_QUEUE_CONCURRENT
用于創(chuàng)建并發(fā)隊列, 在iOS4.3
之前必須傳入NULL
.
使用dispatch_get_main_queue
獲取和主線程相關聯(lián)的主隊列, 注意在主隊列中使用 dispatch_suspend, dispatch_resume, dispatch_set_context
函數(shù), 無效果.
使用dispatch_get_global_queue
獲取全局并發(fā)隊列, 第一個參數(shù)用于設置隊列的優(yōu)先級, 有下列四個值, 第二個參數(shù)傳入0
.
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
2.1 同步執(zhí)行, 串行隊列
- (void)gcdTest1 {
// 串行隊列
dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
}
});
dispatch_sync(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
}
});
dispatch_sync(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
}
});
}
同步執(zhí)行不具備開啟線程的能力, 串行隊列任務一個一個執(zhí)行.
2018-09-03 20:54:20.057889+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058138+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058289+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058442+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058588+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058740+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058882+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.059559+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.060899+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007be40>{number = 1, name = main}
2.2 異步執(zhí)行, 串行隊列
- (void)gcdTest2 {
// 串行隊列
dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
}
});
dispatch_async(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
}
});
dispatch_async(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
}
});
}
異步執(zhí)行具備開啟線程的能力, 串行隊列任務會一個執(zhí)行完成再執(zhí)行另一個.
2018-09-03 20:55:09.989752+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.990425+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.990612+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.990847+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991174+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991261+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991349+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991428+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991548+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2.3 同步執(zhí)行, 并發(fā)隊列
- (void)gcdTest3 {
// 并發(fā)隊列
dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
}
});
dispatch_sync(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
}
});
dispatch_sync(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
}
});
}
同步執(zhí)行不具備開啟線程能力, 在當前線程中執(zhí)行.
2018-09-03 20:55:37.926704+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.926881+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.927068+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.927418+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.927712+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.928126+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.928506+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.929594+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.930951+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007f180>{number = 1, name = main}
2.4 異步執(zhí)行, 并發(fā)隊列
- (void)gcdTest4 {
// 并發(fā)隊列
dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
}
});
dispatch_async(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
}
});
dispatch_async(queue1, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
}
});
}
異步執(zhí)行具備開啟線程的能力, 并發(fā)執(zhí)行任務可以同時執(zhí)行多個.
2018-09-03 20:57:57.697746+0800 ThredDemo[23206:823843] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x604000261c40>{number = 7, name = (null)}
2018-09-03 20:57:57.697818+0800 ThredDemo[23206:823621] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x6000002733c0>{number = 6, name = (null)}
2018-09-03 20:57:57.697916+0800 ThredDemo[23206:823844] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x604000261fc0>{number = 8, name = (null)}
2018-09-03 20:57:57.699438+0800 ThredDemo[23206:823843] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x604000261c40>{number = 7, name = (null)}
2018-09-03 20:57:57.700144+0800 ThredDemo[23206:823621] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x6000002733c0>{number = 6, name = (null)}
2018-09-03 20:57:57.700292+0800 ThredDemo[23206:823844] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x604000261fc0>{number = 8, name = (null)}
2018-09-03 20:57:57.700618+0800 ThredDemo[23206:823843] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x604000261c40>{number = 7, name = (null)}
2018-09-03 20:57:57.700776+0800 ThredDemo[23206:823621] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x6000002733c0>{number = 6, name = (null)}
2018-09-03 20:57:57.700811+0800 ThredDemo[23206:823844] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x604000261fc0>{number = 8, name = (null)}
2.5 一次性函數(shù) dispatch_once
- (void)gcdTest5 {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"任務1--只執(zhí)行了一次哦");
});
}
在進程的生命周期內, 任務1只會執(zhí)行一次, 并且在多線程訪問時, 一次性函數(shù)是安全的.
一次性函數(shù)dispatch_once
經(jīng)常用于創(chuàng)建單例
+ (instancetype)sharedInstance
{
static CTMediator *mediator;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mediator = [[CTMediator alloc] init];
});
return mediator;
}
之前在做App啟動時間優(yōu)化時, 需要將load中的代碼執(zhí)行時間向后推遲, 這時可以使用initialize
和dispatch_once
方法組合, 將load
中的代碼推遲到需要使用的時候再調用.
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 做一些初始化的操作.
});
}
2.6 延遲函數(shù) dispatch_after
- (void)gcdTest6 {
/*
等到指定的時間將任務block異步添加到指定的隊列中.
*/
NSLog(@"begin---");
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), queue, ^{
NSLog(@"延遲函數(shù)--%@", [NSThread currentThread]);
});
NSLog(@"end---");
}
執(zhí)行結果:
2018-09-04 10:50:42.074319+0800 ThredDemo[3066:85334] begin---
2018-09-04 10:50:42.079407+0800 ThredDemo[3066:85334] end---
2018-09-04 10:50:44.278166+0800 ThredDemo[3066:85413] 延遲函數(shù)--<NSThread: 0x604000462640>{number = 3, name = (null)}
延時函數(shù)并非是在指定的時間后開始執(zhí)行任務block, 而是在指定的時間后將任務block異步添加到指定的隊列中, 然后再等待從隊列中取出任務block放入線程中去執(zhí)行, 如果block分配的線程被sleep了一段時間, 那么這個延遲函數(shù)就不太準確了, 所以延遲函數(shù)dispatch_after
存在一定的時間誤差.
2.7 柵欄函數(shù) dispatch_barrier_async
- (void)gcdTest7 {
NSLog(@"begin---");
// 手動創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("com.onealon.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"耗時任務1--%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"耗時任務2--%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"耗時任務1和任務2執(zhí)行完成以后--任務3--%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務4--%@", [NSThread currentThread]);
});
NSLog(@"end---");
}
執(zhí)行結果:
2018-09-04 10:55:25.360533+0800 ThredDemo[3148:88215] begin---
2018-09-04 10:55:25.365543+0800 ThredDemo[3148:88215] end---
2018-09-04 10:55:25.365730+0800 ThredDemo[3148:88889] 耗時任務2--<NSThread: 0x604000271640>{number = 4, name = (null)}
2018-09-04 10:55:25.365730+0800 ThredDemo[3148:88249] 耗時任務1--<NSThread: 0x600000073bc0>{number = 3, name = (null)}
2018-09-04 10:55:25.366010+0800 ThredDemo[3148:88249] 耗時任務1和任務2執(zhí)行完成以后--任務3--<NSThread: 0x600000073bc0>{number = 3, name = (null)}
2018-09-04 10:55:25.367820+0800 ThredDemo[3148:88249] 任務4--<NSThread: 0x600000073bc0>{number = 3, name = (null)}
當柵欄函數(shù)中的任務block到達并發(fā)隊列queue
的最前邊時, 任務block并不會立即執(zhí)行, 它會等待并發(fā)隊列中正在執(zhí)行的任務執(zhí)行完成以后才執(zhí)行柵欄函數(shù)的任務. 任何在柵欄函數(shù)以后添加到并發(fā)隊列中的任務都會等待柵欄函數(shù)中的任務執(zhí)行完成以后才執(zhí)行.
使用dispatch_barrier_async
需要注意:
- 柵欄函數(shù)中攔截的任務必須和耗時任務在同一個隊列中.
- 柵欄函數(shù)中的隊列必須是使用dispatch_queue_create手動創(chuàng)建的并發(fā)隊列
如果是串行隊列或者全局并發(fā)隊列, 那么柵欄函數(shù)就相當于dispatch_async函數(shù), 不會起到攔截效果.
2.8 隊列組
- (void)gcdTest8 {
NSLog(@"begin---");
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務1-%@",[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務2-%@",[NSThread currentThread]);
}
});
dispatch_group_notify(group, queue2, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務3-%@",[NSThread currentThread]);
}
});
NSLog(@"end---");
}
執(zhí)行結果:
2018-09-04 11:12:34.087597+0800 ThredDemo[3435:99939] begin---
2018-09-04 11:12:34.088345+0800 ThredDemo[3435:99939] end---
2018-09-04 11:12:34.088427+0800 ThredDemo[3435:99995] 任務1-<NSThread: 0x60400046e100>{number = 3, name = (null)}
2018-09-04 11:12:34.088427+0800 ThredDemo[3435:100452] 任務2-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.090944+0800 ThredDemo[3435:100452] 任務2-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.090944+0800 ThredDemo[3435:99995] 任務1-<NSThread: 0x60400046e100>{number = 3, name = (null)}
2018-09-04 11:12:34.091614+0800 ThredDemo[3435:99995] 任務1-<NSThread: 0x60400046e100>{number = 3, name = (null)}
2018-09-04 11:12:34.091634+0800 ThredDemo[3435:100452] 任務2-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.091777+0800 ThredDemo[3435:100452] 任務3-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.104841+0800 ThredDemo[3435:100452] 任務3-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.107901+0800 ThredDemo[3435:100452] 任務3-<NSThread: 0x600000266240>{number = 4, name = (null)}
dispatch_group_notify
會監(jiān)聽隊列組group中的任務, 當隊列組中的任務執(zhí)行完成以后, 會將dispatch_group_notify
中的任務添加到指定隊列中.
如果隊列組中沒有任何關聯(lián)的任務, dispatch_group_notify
會立即將任務添加到指定隊列.
dispatch_group_notify
和dispatch_barrier_async
的區(qū)別:
- dispatch_barrier_async只能攔截在同一個隊列中的任務
- dispatch_group_notify可以監(jiān)聽不同隊列中的任務
2.9 GCD中隊列的區(qū)別
主隊列:
- 和主線程相關聯(lián)的隊列
- 不能調用dispatch_suspend, dispatch_resume, dispatch_set_context方法
- 當gcd代碼在主隊列執(zhí)行時, 會將block中的代碼添加到runloop中, runloop調用
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
執(zhí)行block代碼(這部分還需要在研究一下)
全局并發(fā)隊列:
- 全局并發(fā)隊列整個進程共享, 有高 中 低 后臺 四個優(yōu)先級
- 手動創(chuàng)建的隊列可以取消, 全局隊列不能取消(Calls to the dispatch_suspend, dispatch_resume, and dispatch_set_context functions have no effect on the returned queues.)
- 全局并發(fā)隊列不能使用在柵欄函數(shù)
dispatch_barrier_async
中
三. NSOperation
NSOperation
的使用步驟:
- 使用
NSOperation
的子類創(chuàng)建操作operation - 使用
NSOperationQueue
創(chuàng)建隊列queue - 將操作operation添加到隊列queue中, 系統(tǒng)會自動去除隊列中的任務執(zhí)行.
NSOperation
是一個抽象類, 在使用的時候可以使用它的子類NSBlockOperation
和NSInvocationOperation
, 或者創(chuàng)建NSOperation
的子類.
NSOperationQueue
分兩種隊列, 一種是通過[NSOperationQueue mainQueue]
獲取的主隊列(主隊列是串行隊列), 另一種是通過[[NSOperationQueue alloc] init]
手動創(chuàng)建的隊列. 手動創(chuàng)建的隊列可以通過設置maxConcurrentOperationCount
屬性控制器隊列是串行還是并發(fā), maxConcurrentOperationCount=1
是串行隊列.
3.1 NSBlockOperation
- (void)operationTest1 {
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務1-%@",[NSThread currentThread]);
}
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務2-%@",[NSThread currentThread]);
}
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
[queue addOperation:operation1];
[queue addOperation:operation2];
}
3.2 NSInvocationOperation
- (void)operationTest2 {
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation1Selector) object:nil];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation2Selector) object:nil];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
}
- (void)operation1Selector {
for (int i = 0; i < 3; i++) {
NSLog(@"任務1-%@",[NSThread currentThread]);
}
}
- (void)operation2Selector {
for (int i = 0; i < 3; i++) {
NSLog(@"任務2-%@",[NSThread currentThread]);
}
}
3.3 匿名操作
- (void)operationTest3 {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務1-%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務2-%@",[NSThread currentThread]);
}
}];
}
3.4 取消 暫停 恢復操作
- (void)operationTest5 {
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務1-%@",[NSThread currentThread]);
}
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務2-%@",[NSThread currentThread]);
}
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 取消某一個操作
// [operation1 cancel];
// 暫停隊列
[queue setSuspended:YES];
// 取消隊列中的操作
// [queue cancelAllOperations];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[queue setSuspended:NO];
});
[queue addOperation:operation1];
[queue addOperation:operation2];
}
3.5 設置依賴
- (void)operationTest4 {
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務1-%@",[NSThread currentThread]);
}
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務2-%@",[NSThread currentThread]);
}
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務3-%@",[NSThread currentThread]);
}
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[operation2 addDependency:operation3];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
}
3.6 線程間通訊
- (void)operationTest6 {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"在子線程中執(zhí)行--任務1--%@", [NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"在主線程中執(zhí)行--任務2--%@", [NSThread currentThread]);
}];
}];
}
四. 線程死鎖問題
在使用多線程的時候要特別注意線程死鎖的問題, 以下代碼均在主線程執(zhí)行, 會死鎖嗎?
- (void)interview1 {
NSLog(@"執(zhí)行任務1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"執(zhí)行任務2");
});
NSLog(@"執(zhí)行任務3");
}
會造成線程死鎖的現(xiàn)象
gcd代碼同步執(zhí)行, 會阻塞當前線程, 也就是任務2執(zhí)行完成以后才會執(zhí)行任務3. 主隊列是串行隊列, 當一個任務執(zhí)行完成以后才會執(zhí)行另一個任務, 也就是任務3執(zhí)行完成以后才會執(zhí)行任務2. 所以任務2和任務3就會一直相互等待, 形成死鎖.
- (void)interview2 {
NSLog(@"執(zhí)行任務1");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"執(zhí)行任務2");
});
NSLog(@"執(zhí)行任務3");
}
不會造成死鎖
gcd代碼異步執(zhí)行, 不會阻塞當前線程, 個人理解為gcd代碼會將任務2添加到主隊列中, 而interview2也是在主隊列中, 主隊列中的任務執(zhí)行順序是一個一個執(zhí)行, 所以, 執(zhí)行順序是1-3-2
- (void)interview3
{
NSLog(@"執(zhí)行任務1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務2");
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務3");
});
NSLog(@"執(zhí)行任務4");
});
NSLog(@"執(zhí)行任務5");
}
任務2不會死鎖, 任務3會死鎖.
queue是串行隊列, 執(zhí)行任務是一個一個執(zhí)行, async代碼是異步執(zhí)行, 會開啟新線程, 任務2是在子線程中執(zhí)行, sync代碼是同步執(zhí)行, 不會開啟新線程, 阻塞當前線程, 任務3會等待隊列中的async任務執(zhí)行完成以后, 再取出sync代碼執(zhí)行, 而sync代碼已經(jīng)阻塞了當前線程, 所以造成死鎖.
- (void)interview4
{
NSLog(@"執(zhí)行任務1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務2");
dispatch_sync(queue2, ^{
NSLog(@"執(zhí)行任務3");
});
NSLog(@"執(zhí)行任務4");
});
NSLog(@"執(zhí)行任務5");
}
不會造成死鎖
queue和queue2雖然都是串行隊列, async+queue會開啟新線程, sync雖然阻塞當前線程, 但是sync的任務是從queue2隊列中獲取, 不會造成死鎖
- (void)interview5
{
NSLog(@"執(zhí)行任務1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務2");
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務3");
});
NSLog(@"執(zhí)行任務4");
});
NSLog(@"執(zhí)行任務5");
}
不會造成死鎖
queue是并發(fā)隊列, 并發(fā)隊列可以同時執(zhí)行多個任務, async+queue會開啟新線程, 在子線程執(zhí)行async代碼, sync雖然會阻塞當前線程, 但是queue是并發(fā)隊列, 所以執(zhí)行任務3時不必等待async代碼從隊列中執(zhí)行完成.