信号量(Dispatch Semaphore)
GCD中信号量,指的是 Dispatch Semaphore ,是持有计数的信号。
信号量提供了三个函数
应用
- 保持线程同步,将异步任务转换为同步执行任务
- 保证线程安全,为线程加锁
保持线程同步
异步任务转换为同步 执行任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| dispatch_semaphore_create: 创建一个 Semaphore 并初始化信号的总量。
dispatch_semaphore_signal: 发送一个信号,让信号总量 +1。
dispatch_semaphore_wait: 可以使总信号量减1, 当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。 代码: -(void)dispatch_semaphore{ // 信号量(注意:初始化信号量为0) dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSInteger number = 0; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ number = 100; NSLog(@"%@",[NSThread currentThread]); sleep(4); // 延迟4秒,再解锁方便查看打印, 确实是加锁阻塞了当前线程 dispatch_semaphore_signal(semaphore);// 解锁 }); // 加锁 NSLog(@"%@",[NSThread currentThread]); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore --- end number = %zd",number); }
|
打印结果
保证线程安全,为线程加锁
在线程安全中可以将dispatch_semaphore_wait 看作加锁, dispatch_semaphore_signal看作解锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // 注意:初始化信号量为1 _semaphore = dispatch_semaphore_create(1); for (NSInteger i = 0 ; i < 100; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self asyncTask]; }); } }
-(void)asyncTask { dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); _count++; sleep(1); NSLog(@"执行任务:%zd",_count); dispatch_semaphore_signal(_semaphore); }
|
打印结果
原因:
在子线程中并发执行 asyncTask,那么第一个添加到并发队列的,会将信号量 -1,此时信号量等于0,可以执行接下来的任务。而并发队列中其他任务,由于此时信号量不等于0, 必须等当前正在执行的任务执行完毕后调用dispatch_semaphore_signal 将信号量加1,才可以继续执行接下来的任务,以此类推,从而达到线程加锁的目的。
再举一个例子:
多个任务1-10,等全部任务结束之后再往下执行其他任务 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); //创建一个并发队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //开启的是10个异步的操作,通过信号量,让10个异步的最多1个m,剩下的同步等待 for (NSInteger i = 0; i < 10; i++) { dispatch_async(queue, ^{ //当信号量为0时候,阻塞当前线程 NSLog(@"执行任务 %ld", i); sleep(1); NSLog(@"完成当前任务 %ld", i); _count++;
//释放信号 if (_count == 10){ dispatch_semaphore_signal(semaphore); } }); } dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"end------");
NSLog(@"go on next...");
|
执行结果为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 执行任务 0 执行任务 2 执行任务 1 执行任务 5 执行任务 7 执行任务 6 执行任务 3 执行任务 4 执行任务 8 执行任务 9 完成当前任务 9 完成当前任务 2 完成当前任务 1 完成当前任务 3 完成当前任务 7 完成当前任务 5 完成当前任务 0 完成当前任务 4 完成当前任务 8 完成当前任务 6 end------ go on next...
|
总结;
信号量可以做:
- 异步队列顺序执行,不阻塞主线程。
- 异步无序执行,阻塞主线程,执行完全部任务之后再做后续任务。