一:基础名称
任务就是block内执行的操作,block内调用的某个方法。任务有两种方式,一为同步执行
,二为异步执行
。二者的区别在于是否具备开启子线程的能力,执行的任务在队列中执行的方式(顺序)。
同步执行的特点
异步执行的特点:
可以看出,同步执行一定是在当前指定线程中执行,而异步虽然具备开启子线程的能力,具体还要与指定的队列相关。
队列的结构形式即数据结构中的队列一样,遵循先进先出的原则。即一个队列中的任务第一个被追加到队列中,则它应该第一个被完成释放。而GCD中存在着两种队列,一为串行队列
,二为并行队列
。二者的区别在于执行顺序与开启的线程所在位置不一样。
串行队列的特点:
并行队列的特点:
然而,在GCD里面还存在着两个特殊的队列,即属于串行队列的主队列
,属于并发队列的全局并发队列
。主队列属于串行队列,但是iOS中程序是在主队列里面执行就造就了“主队列”的特殊。
1.1 dispatch_queue_t
该函数用以创建队列对象,可为串行队列,并发队列,主队列,串行队列
1.2 dispatch_group_t
1.3 dispatch_group_async
1.4 dispatch_group_enter
该函数标志着一个任务追加到group,执行一次,即group中未执行完毕的任务数+1
1.5 dispatch_group_leave
该函数标志着一个任务已经被完成,需要提醒group移除一个任务数,即group中未执行完毕的任务数-1
1.6 dispatch_group_notify
当group中任务数为0时,会调用notify执行block,常用于回到主线程,即解除了dispatch_group_wait
1.7 dispatch_semaphore_wait
该函数使得被调用的线程加锁,当semaphore信号量小于0时被阻塞
1.8 dispatch_semaphore_signal
该函数相当于被调用线程结束,会使得semaphore信号量+1
二: 排列方式
并发队列 | 串行队列 | 主队列 | |
---|---|---|---|
同步执行 | 未开启子线程;串行执行任务 | 未开启子线程;串行地执行任务 | 死锁 |
异步执行 | 开启了子线程;任务并行地执行 | 开启了子线程;串行执行任务 | 未开启子线程;任务串行地执行 |
2.1 同步 + 并发队列
说明:
-
虽然队列指定为并发队列,但是执行方式为同步执行,同步执行不具备开启线程的能力,故任务只能在当前线程以串行方式一个接一个地执行。
2.2 异步 + 并发队列
说明:
2.3 同步 + 串行队列
说明:
-
该组合不会开启新的线程去执行,因为执行方式是同步执行,该方式不具备开启新线程的能力;而串行队列决定了线程个数只有一个,故任务会挨个执行。
2.4 异步 + 串行队列
说明:
2.5 同步 + 主队列
说明:
-
这种组合方式如果以方法形式直接在主线程里面调用会造成死锁。即主线程里面等待方法执行完毕,而方法里面的任务等待主线程处理完方法,二者互相等待,直接死锁。
-
但如果在组合形式是以子线程形式调用,则不会发生死锁。原因即:新开启的子线程等待方法执行完毕,而方法内部的任务是在主线程内执行,待主线程内的任务执行完毕,新开启的子线程也执行完毕,也不会发生死锁。
2.6 异步 + 主队列
说明:
三:常见异步业务场景与编码
3.1 异步线程通信
异步线程通信的业务场景处处可见,以最常见的网络请求为例,我们通常会开启一个子线程用以请求网络,请求完毕后拿到请求数据回到主线程,供主线程刷新UI。
/**
线程间通信
- 在全局并发队列执行任务,执行完毕后会到主线程操作数据
*/
- (void)asyncDoAndAppendToMainQueue{
dispatch_queue_t global_concurrent_queue = dispatch_queue_create("DIS", DISPATCH_QUEUE_PRIORITY_DEFAULT);
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(global_concurrent_queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"------> THREAD: %@", [NSThread currentThread]);
NSInteger a = 1;
a++;
dispatch_async(main_queue, ^{
NSLog(@"------> value: %d", a);
NSLog(@"------> THREAD: %@", [NSThread currentThread]);
});
});
}
代码说明:首先创建一个全局队列,接着定义一个主队列。在dispatch_async函数里面传入全局队列用以发起网络请求。在此模拟网络请求过程,耗时两秒使得a++,然后异步回到主线层并打印数据。结果如下:
2022-03-15 15:47:47.819016+0800 GCDios[17679:8316624] ------> THREAD: <NSThread: 0x2812ac380>{number = 9, name = (null)}
2022-03-15 15:47:47.819246+0800 GCDios[17679:8316613] ------> value: 2
2022-03-15 15:47:47.819345+0800 GCDios[17679:8316613] ------> THREAD: <NSThread: 0x2812fc900>{number = 1, name = main}
3.2 异步线程组合请求并回调通知
有时候一个VC或者模块发起的并不单单一个请求,而是多个请求,如何将多个请求结果包装为一个统一回调参数或对象是个问题,使用异步组合请求可以很好地解决(出现这样的业务场景是因为这个VC或模块在初始化的时候需要多个参数或特殊的模型,而初始化仅有一次,不可能在后续中再次请求刷新)。
/**
异步执行全局并发队列,enter leave后notify回到主线程
*/
- (void)asyncDoGroupWithEnterLeaveAndAppendToMainQueue{
dispatch_queue_t global_concurrent_queue = dispatch_queue_create("DIS", DISPATCH_QUEUE_PRIORITY_DEFAULT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, global_concurrent_queue, ^{
NSInteger a = 0;
a++;
// 未完成任务数+1,标志着notify不会被调用
dispatch_group_enter(group);
[NSThread sleepForTimeInterval:2];
NSLog(@"------> value: %d", a);
NSLog(@"------> THREAD: %@n", [NSThread currentThread]);
// 该耗时任务已完成,应该从group队列里面移除
dispatch_group_leave(group);
// 再次放入一个任务到group队列里
dispatch_group_enter(group);
[NSThread sleepForTimeInterval:2];
a++;
NSLog(@"------> value: %d", a);
NSLog(@"------> THREAD: %@n", [NSThread currentThread]);
// 任务结束,从group立main移除,则会通知notify调用
dispatch_group_leave(group);
// 将group队列任务操纵的数据带回到主线程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"------> value: %d", a);
NSLog(@"------> THREAD: %@n", [NSThread currentThread]);
});
});
}
可以看到,刚开始的时候group内任务数为0,然后调用dispatch_group_enter(group)
,使得该group内任务数+1,而group内任务数为非0的情况下是不会执行dispatch_group_notify
方法的。当第一个模拟网络请求结束时,调用dispatch_groyp_leave(group)
使得任务数-1以便执行下一个模拟网络请求。待第二个网络请求结束后,group内任务数为0,此时便执行了dispatch_group_notify
方法回到主线程。如果我们这个示例方法内包含一个block参数,并在notify内回调,那么外层模块就会等到所有请求结束后才能拿到参数或模型。
结果如下:
2022-03-15 15:54:35.889857+0800 GCDios[17681:8317881] ------> value: 1
2022-03-15 15:54:35.890880+0800 GCDios[17681:8317881] ------> THREAD: <NSThread: 0x281fa0680>{number = 8, name = (null)}
2022-03-15 15:54:37.896368+0800 GCDios[17681:8317881] ------> value: 2
2022-03-15 15:54:37.897212+0800 GCDios[17681:8317881] ------> THREAD: <NSThread: 0x281fa0680>{number = 8, name = (null)}
2022-03-15 15:54:37.898013+0800 GCDios[17681:8317865] ------> value: 2
2022-03-15 15:54:37.898510+0800 GCDios[17681:8317865] ------> THREAD: <NSThread: 0x281ff02c0>{number = 1, name = main}
3.3 异步任务通过dispatch_semaphore转同步
有时候业务为执行一个异步操作,但需要该异步操作的结果以供下面的代码调用,这就是异步任务转同步任务。
/**
异步任务通过semaphore转同步任务
*/
- (void)asyncTransferSyncBySemaphore{
// 1. 当前为主线程
NSLog(@"------> currentThread: %@", [NSThread currentThread]);
// 2. 创建全局队列用以执行子线程任务
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 3. 创建semaphore为0的信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 4. 定义资源
__block NSInteger a = 20;
dispatch_async(queue, ^{
// 4.2 模拟耗时操作
[NSThread sleepForTimeInterval:2];
NSLog(@"------> Thread: %@", [NSThread currentThread]);
a++;
// 4.4 此时主线程已经卡住,执行这一步后semaphore+1,总和为0,主线程恢复
dispatch_semaphore_signal(semaphore);
});
// 4.1 上述子线程与这里的主线程会同步执行,但是子线程里和耗时操作尚未操纵资源a,此时a为20
NSLog(@"------> Before wait Thread: %@ AND PARA A:%d", [NSThread currentThread], a);
// 4.3 信号量-1,主线程堵塞,当过不了多久子线程里耗时操作就会完成,其后操纵资源a,并使semaphore+1,使主线程开放
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 4.5 主线程通路后资源a已被子线程通过耗时操作操纵,此时a为21
NSLog(@"------> After wait Thread: %@ AND PARA A:%d", [NSThread currentThread], a);
}
如果最后一个Log语句打印的参数a为21,即证明了我们成功地将异步任务转换为了同步任务,而下面的结果也恰恰佐证了这一方法。
结果如下:
2022-03-15 16:55:45.889530+0800 GCDios[17704:8329687] ------> currentThread: <NSThread: 0x2818ac900>{number = 1, name = main}
2022-03-15 16:55:45.889676+0800 GCDios[17704:8329687] ------> Before wait Thread: <NSThread: 0x2818ac900>{number = 1, name = main} AND PARA A:20
2022-03-15 16:55:47.894895+0800 GCDios[17704:8329702] ------> Thread: <NSThread: 0x2818e9f80>{number = 6, name = (null)}
2022-03-15 16:55:47.895247+0800 GCDios[17704:8329687] ------> After wait Thread: <NSThread: 0x2818ac900>{number = 1, name = main} AND PARA A:21
3.4 异步线程安全之加锁解锁
竞争性的资源在多线程修改时永远不安全,可能会出现各式各样奇怪的问题。在此,模拟出一种常见的业务,使用semaphore信号量加锁来保证竞争资源的安全。
我们此时拥有车辆为20辆,拥有两家门店store_1_queue与store_2_queue,每家门店当顾客预订车辆时会使得carCount自减1,当carCount为0时不再提供预订服务,Log没有车辆。
/// 模拟门店预约车辆服务
- (void)bookCar{
NSLog(@"------> FIRST: %@ AT THREAD: %@", self.semaphore, [NSThread currentThread]);
while (1) {
if (self.carCount > 0) {
self.carCount--;
NSLog(@"------> Store: %@, Used Count: %d", [NSThread currentThread], self.carCount);
[NSThread sleepForTimeInterval:0.2];
} else {
NSLog(@"------> Store: %@ has no car.", [NSThread currentThread]);
break;
}
}
}
上面使用了self.semaphore与self.carCount,在viewDidLoad方法内不初始化一下即可,如下:
self.carCount = 20;
self.semaphore = dispatch_semaphore_create(1);
门店的代码如下:
- (void)threadSafe{
// 创建门店1、门店2串行队列用以提供购买方式
dispatch_queue_t store_1_queue = dispatch_queue_create("queue.dayueceng", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t store_2_queue = dispatch_queue_create("queue.changfenggongyuan", DISPATCH_QUEUE_SERIAL);
// 门店1异步执行订车服务
dispatch_async(store_1_queue, ^{
[self bookCar];
});
// 门店2异步执行订车服务
dispatch_async(store_2_queue, ^{
[self bookCar];
});
}
当在viewDidLoad内调用threadSafe方法看似没问题,每个门店在各自的线程去执行bookCar方法直至self.carCount为0然后Log没有车辆。但是问题严重在于每个门店线程可能在同一时刻访问self.carCount,即会出现store_1_queue预订的时候车辆总数为16,而接下来当store_2_queue预订的时候车辆总数为17,这明显不缝合逻辑的。因此需要使用semaphore信号量加锁,使得在同一时刻只有唯一一个线程去访问修改self.carCount。完整代码如下:
@interface ViewController ()
@property (nonatomic, assign) NSInteger carCount;
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self initData];
[self threadSafe];
}
- (void)initData{
self.carCount = 20;
self.semaphore = dispatch_semaphore_create(1);
}
/**
线程安全示例
- 异步访问竞争资源并更新
*/
- (void)threadSafe{
// 创建门店1、门店2串行队列用以提供购买方式
dispatch_queue_t store_1_queue = dispatch_queue_create("queue.dayueceng", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t store_2_queue = dispatch_queue_create("queue.changfenggongyuan", DISPATCH_QUEUE_SERIAL);
// 门店1异步执行订车服务
dispatch_async(store_1_queue, ^{
[self bookCar];
});
// 门店2异步执行订车服务
dispatch_async(store_2_queue, ^{
[self bookCar];
});
}
/// 模拟门店预约车辆服务
- (void)bookCar{
// 对于每一个门店线程而言,其操纵预定的是同一个竞争资源self.carCount,只要保证在同一时刻只有唯一一个线程去访问修改self.carCount即可保证线程安全
// 则使用wait与singal保证某个门店线程在预订期间semaphore为唯一访问者即可。
NSLog(@"------> FIRST: %@ AT THREAD: %@", self.semaphore, [NSThread currentThread]);
while (1) {
// 加锁,semaphore
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"------> WAIT: %@ AT THREAD: %@", self.semaphore, [NSThread currentThread]);
if (self.carCount > 0) {
self.carCount--;
NSLog(@"------> Store: %@, Used Count: %d", [NSThread currentThread], self.carCount);
[NSThread sleepForTimeInterval:0.2];
} else {
NSLog(@"------> Store: %@ has no car.", [NSThread currentThread]);
dispatch_semaphore_signal(self.semaphore);
break;
}
// 解锁
dispatch_semaphore_signal(self.semaphore);
NSLog(@"------> SIGNAL: %@ AT THREAD: %@", self.semaphore, [NSThread currentThread]);
}
}
可以看到虽然每个门店线程都访问了bookCar方法,但是通过wait使得每个门店线程在访问期间从加锁-防止其他门店预订-本门店预订-解锁
这个流程一直走下去,直到无车辆可供调度。
结果如下:
2022-03-15 16:20:19.170210+0800 GCDios[17690:8322635] ------> FIRST: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:19.170332+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:19.170381+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 19
2022-03-15 16:20:19.170467+0800 GCDios[17690:8322634] ------> FIRST: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:19.371599+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:19.371779+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:19.371850+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 18
2022-03-15 16:20:19.572499+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:19.572502+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:19.572929+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 17
2022-03-15 16:20:19.778504+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:19.778827+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:19.779001+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 16
2022-03-15 16:20:19.984579+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:19.984587+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:19.985258+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 15
2022-03-15 16:20:20.191370+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:20.191516+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:20.192384+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 14
2022-03-15 16:20:20.393759+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:20.393768+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:20.394415+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 13
2022-03-15 16:20:20.597513+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:20.597750+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:20.598420+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 12
2022-03-15 16:20:20.804358+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:20.804525+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:20.805148+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 11
2022-03-15 16:20:21.011005+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:21.011204+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:21.011824+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 10
2022-03-15 16:20:21.217719+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:21.218465+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:21.218938+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 9
2022-03-15 16:20:21.424744+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:21.424779+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:21.425369+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 8
2022-03-15 16:20:21.631025+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:21.631465+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:21.631723+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 7
2022-03-15 16:20:21.834804+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:21.834888+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:21.835452+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 6
2022-03-15 16:20:22.041551+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:22.042263+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:22.042735+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 5
2022-03-15 16:20:22.248610+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:22.248646+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:22.249263+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 4
2022-03-15 16:20:22.455297+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:22.455332+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:22.455917+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 3
2022-03-15 16:20:22.661461+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:22.661466+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:22.661857+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 2
2022-03-15 16:20:22.867473+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:22.867513+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:22.867893+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)}, Used Count: 1
2022-03-15 16:20:23.073678+0800 GCDios[17690:8322635] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:23.073685+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:23.074345+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)}, Used Count: 0
2022-03-15 16:20:23.278227+0800 GCDios[17690:8322634] ------> SIGNAL: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:23.278236+0800 GCDios[17690:8322635] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d86ac0>{number = 7, name = (null)}
2022-03-15 16:20:23.279220+0800 GCDios[17690:8322635] ------> Store: <NSThread: 0x280d86ac0>{number = 7, name = (null)} has no car.
2022-03-15 16:20:23.280028+0800 GCDios[17690:8322634] ------> WAIT: <OS_dispatch_semaphore: 0x283bdf390> AT THREAD: <NSThread: 0x280d84b00>{number = 8, name = (null)}
2022-03-15 16:20:23.280499+0800 GCDios[17690:8322634] ------> Store: <NSThread: 0x280d84b00>{number = 8, name = (null)} has no car.
3.5 dispatch_barrier_async
有时候业务场景为先执行一组异步任务,接着再执行另外一组异步任务。这时候便可使用dispoatch_barrier_async进行栅栏分割操作。
- (void)barrierAysnc{
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.barrier", DISPATCH_QUEUE_CONCURRENT);
__block NSInteger a = 0;
dispatch_async(concurrent_queue, ^{
[NSThread sleepForTimeInterval:2];
a++;
NSLog(@"------> 1 CURRENT THREAD: %@", [NSThread currentThread]);
});
dispatch_async(concurrent_queue, ^{
[NSThread sleepForTimeInterval:2];
a++;
NSLog(@"------> 2 CURRENT THREAD: %@", [NSThread currentThread]);
});
dispatch_async(concurrent_queue, ^{
[NSThread sleepForTimeInterval:5];
a++;
NSLog(@"------> 3 CURRENT THREAD: %@", [NSThread currentThread]);
});
dispatch_barrier_sync(concurrent_queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"------> value:%ld barrier CURRENT THREAD: %@", (long)a, [NSThread currentThread]);
});
dispatch_async(concurrent_queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"------> 4 CURRENT THREAD: %@", [NSThread currentThread]);
});
dispatch_async(concurrent_queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"------> 5 CURRENT THREAD: %@", [NSThread currentThread]);
});
}
在dispatch_barrier_async
执行之前,开启的子线层会对被操作资源(a)进行操作,dispatch_barrier_async
执行的时候,会利用资源进行进一步操作;此过程完毕,会执行第二组异步操作。
结果如下:
2022-03-21 15:28:08.445063+0800 GCDios[18226:9521504] ------> 1 CURRENT THREAD: <NSThread: 0x2838c0140>{number = 5, name = (null)}
2022-03-21 15:28:08.445146+0800 GCDios[18226:9521499] ------> 2 CURRENT THREAD: <NSThread: 0x2838c0d40>{number = 6, name = (null)}
2022-03-21 15:28:11.445473+0800 GCDios[18226:9521503] ------> 3 CURRENT THREAD: <NSThread: 0x2838c0b80>{number = 7, name = (null)}
2022-03-21 15:28:13.447573+0800 GCDios[18226:9521489] ------> value:3 barrier CURRENT THREAD: <NSThread: 0x2838808c0>{number = 1, name = main}
2022-03-21 15:28:15.452746+0800 GCDios[18226:9521503] ------> 4 CURRENT THREAD: <NSThread: 0x2838c0b80>{number = 7, name = (null)}
2022-03-21 15:28:15.452800+0800 GCDios[18226:9521499] ------> 5 CURRENT THREAD: <NSThread: 0x2838c0d40>{number = 6, name = (null)}
四:dispatch_semaphore浅谈
4.1 dispatch_semaphore_t
dispatch_semaphore_t
用来声明dispatch_semaphore函数,创建则使用dispatch_semaphore_create
函数,该函数依赖于结构体dispatch_semaphore_s,后者结构体声明如下:
struct dispatch_semaphore_s {
DISPATCH_STRUCT_HEADER(semaphore);
semaphore_t dsema_port; //等同于mach_port_t信号
long dsema_orig; //初始化的信号量值
long volatile dsema_value; //当前信号量值
union {
long volatile dsema_sent_ksignals;
long volatile dsema_group_waiters;
};
struct dispatch_continuation_s *volatile dsema_notify_head; //notify的链表头部
struct dispatch_continuation_s *volatile dsema_notify_tail; //notify的链表尾部
};
dispatch_semaphore_create函数内部申请地址的时候会用到dispatch_semaphore_s结构体,其dsma变量会根据dispatch_semaphore计算出合适的空间。dispatcH-semaphore_t的函数内部如下:
dispatch_semaphore_t dispatch_semaphore_create(long value) {
dispatch_semaphore_t dsema;
if (value < 0) {
//value值需大于或等于0
return NULL;
}
//申请dispatch_semaphore_t的内存
dsema = (dispatch_semaphore_t)_dispatch_alloc(DISPATCH_VTABLE(semaphore),
sizeof(struct dispatch_semaphore_s) -
sizeof(dsema->dsema_notify_head) -
sizeof(dsema->dsema_notify_tail));
//调用初始化函数
_dispatch_semaphore_init(value, dsema);
return dsema;
}
//初始化结构体信息
static void _dispatch_semaphore_init(long value, dispatch_object_t dou) {
dispatch_semaphore_t dsema = dou._dsema;
dsema->do_next = (dispatch_semaphore_t)DISPATCH_OBJECT_LISTLESS;
dsema->do_targetq = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dsema->dsema_value = value; //设置信号量的当前value值
dsema->dsema_orig = value; //设置信号量的初始value值
}
从这里我们可以看到,当调用dispatch_semaphore_create()
函数时,若初始化值小于1则会返回空,这是我们需要注意的地方。而另一个需要注意的地方为dispatch_semaphore
销毁的时候。销毁时调用函数如下:
//释放信号量的函数
void _dispatch_semaphore_dispose(dispatch_object_t dou) {
dispatch_semaphore_t dsema = dou._dsema;
if (dsema->dsema_value < dsema->dsema_orig) {
//Warning:信号量还在使用的时候销毁会造成崩溃
DISPATCH_CLIENT_CRASH(
"Semaphore/group object deallocated while in use");
}
kern_return_t kr;
if (dsema->dsema_port) {
kr = semaphore_destroy(mach_task_self(), dsema->dsema_port);
DISPATCH_SEMAPHORE_VERIFY_KR(kr);
}
}
可以看到,若当前信号量值小于初始化值时会崩溃,因此一定要慎重注意到设置dispatch_semaphore的值在信号量尚在使用时不可设置其为nil或重新赋值。
4.2 dispatch_semaphore_wait
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout){
long value = dispatch_atomic_dec2o(dsema, dsema_value, acquire);
if (fastpath(value >= 0)) {
return 0;
}
return _dispatch_semaphore_wait_slow(dsema, timeout);
}
dispatch_semaphore_wait
先将信号量的dsema值原子性减一,并将新值赋给value。如果value大于等于0就立即返回,否则调用_dispatch_semaphore_wait_slow
函数,等待信号量唤醒或者timeout超时。_dispatch_semaphore_wait_slow
函数的实现较为复杂,这里不再展开赘述。
4.3 dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_t dsema) {
long value = dispatch_atomic_inc2o(dsema, dsema_value, release);
if (fastpath(value > 0)) {
return 0;
}
if (slowpath(value == LONG_MIN)) {
DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_semaphore_signal()");
}
return _dispatch_semaphore_signal_slow(dsema);
}
首先将dsema_value调用原子方法加1,如果大于零就立即返回0,否则进入_dispatch_semaphore_signal_slow
函数,该函数会调用semaphore_signal
函数唤醒在dispatch_semaphore_wait
中等待的线程。_dispatch_semaphore_signal_slow
同样不再赘述。
五:dispatch_group浅谈
dispatch_group
的创建实际上是依赖于dispatch_semaphore
,下面就常用函数进行浅谈。
5.1 dispatch_group_create
dispatch_group_t dispatch_group_create(void) {
return (dispatch_group_t)dispatch_semaphore_create(LONG_MAX);
}
可以看到,dispatch_group_create
就是创造了以LONG_MAX
为信号量的dispatch_semaphore,至于为什么信号量为LONG_MAX
,猜测可能是为了防止使用者过多地调用而崩溃特意将信号量调大。
5.2 dispatch_group_enter
void dispatch_group_enter(dispatch_group_t dg) {
dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
(void)dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
}
可以看到,dispatch_group实际上就是以当前group为参数,发出wait信号量。
5.3 dispatch_group_leave
void dispatch_group_leave(dispatch_group_t dg) {
dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
dispatch_atomic_release_barrier();
long value = dispatch_atomic_inc2o(dsema, dsema_value);
//dsema_value原子性加1
if (slowpath(value == LONG_MIN)) {
//内存溢出,由于dispatch_group_leave在dispatch_group_enter之前调用
DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_group_leave()");
}
if (slowpath(value == dsema->dsema_orig)) {
//表示所有任务已经完成,唤醒group
(void)_dispatch_group_wake(dsema);
}
}
可以看到 dispatch_group_leave
将 dispatch_group_t
转换成 dispatch_semaphore_t
后将 dsema_value 的值原子性加1。如果 value 为 LONG_MIN 则崩溃;如果 value 等于 dsema_orig 表示所有任务已完成,调用 _dispatch_group_wake
唤醒group(即唤醒notify函数)
原文地址:https://blog.csdn.net/kicinio/article/details/123497594
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_35580.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!