iOS Dispatch Semaphore使用
Dispatch Semaphore介绍
Dispatch Semaphore 是持有计数的信号,是多线程编程中的计数类类型信号。所谓信号,即类似于小区出入口的栅栏。可通过时举起栅栏,不可通过时候放下栅栏。Dispatch Semaphore 中,使用计数来实现该功能。计数为0时等待,计数为1或大于1时,减去1而不等待。
使用方法
下面是创建一个dispatch_semaphore_t 对象,参数表示计数的初始值,下面例子初始化值为1。从dispatch_semaphore_create方法名可看出,该函数与 dispatch_group_t和 dispatch_queue_t一样,在非ARC模式下必须通过dispatch_release函数释放。也可以使用 dispatch_retain函数持有。
dispatch_semaphore_t semaphore_t=dispatch_semaphore_create(1);
dispatch_semaphore_wait 函数等待 dispatch_semaphore_t 的计数值大于或等于1。当计数值大于等于1,或者在待机中计数值大于等于1时,对计数进行减法操作并从 dispatch_semaphore_wait函数返回。 第二个参数和 dispatch_group_wait一样,由dispatch_time_t 类型值指定等待时间。本例中的参数值意味着永久等待。 dispatch_semaphore_wait和dispatch_group_wait 函数的返回值也一样。
dispatch_semaphore_wait(semaphore_t, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait 函数返回0时,可安全执行所需的排他控制的处理。等处理结束时,通过dispatch_semaphore_signal函数将 dispatch_semaphore_t 的计数值加1。
使用案例
//创建队列
dispatch_queue_t queue =dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建dispatch_semaphore_t 信号对象,并设置计数初始化值为1
dispatch_semaphore_t semaphore_t=dispatch_semaphore_create(1);
NSMutableArray *array=[[NSMutableArray alloc]init];
for (int i=0; i<50000; i++) {
dispatch_async(queue, ^{
//等待semaphore_t,直到 semaphore_t 计数值大于等于1,目的保证可访问array对象的线程同时只有1个
dispatch_semaphore_wait(semaphore_t, DISPATCH_TIME_FOREVER);
//因semaphore_t 大于等于1,所以将semaphore_t计数值减去1,dispatch_semaphore_wait执行返回。到此,dispatch_semaphore_t 的计数值恒为0。可以安全的进行array 数据更新
[array addObject:@(i)];
//排他控制处理结束,dispatch_semaphore_signal 将semaphore_t 的计数值加1。
//等待dispatch_semaphore_t 的计数值增加的线程,就由最先等待的线程执行。
dispatch_semaphore_signal(semaphore_t);
});
}
dispatch_semaphore_t 死锁问题改进
例:死锁
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue=dispatch_queue_create("111111", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"b1 %@", [NSThread currentThread]);
[self getUserDepartmentWithSemaphore:semaphore];
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"b2 %@", [NSThread currentThread]);
[self getMyDepartmentListWithSemaphore:semaphore];
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
if (self.myDepartment!=nil) {
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.selectedDepartment=self.myDepartment;
self.companyFileHeaderView.title=self.selectedDepartment.name;
}];
[self loadNewData];
}
dispatch_semaphore_signal(semaphore);
NSLog(@"b3 %@", [NSThread currentThread]);
});
2021-04-23 18:09:05.509692+0800 IQHIPan[15507:322370] b1 <NSThread: 0x6000027f01c0>{number = 7, name = (null)}
例:改进后
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue=dispatch_queue_create("111111", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"b1 %@", [NSThread currentThread]);
[self getUserDepartmentWithSemaphore:semaphore];
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"b2 %@", [NSThread currentThread]);
[self getMyDepartmentListWithSemaphore:semaphore];
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (self.myDepartment!=nil) {
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.selectedDepartment=self.myDepartment;
self.companyFileHeaderView.title=self.selectedDepartment.name;
}];
[self loadNewData];
}
dispatch_semaphore_signal(semaphore);
NSLog(@"b3 %@", [NSThread currentThread]);
});
返回结果
2021-04-23 18:11:21.149867+0800 IQHIPan[15592:324628] b1 <NSThread: 0x600003d1c200>{number = 5, name = (null)}
2021-04-23 18:11:21.445690+0800 IQHIPan[15592:324628] b2 <NSThread: 0x600003d1c200>{number = 5, name = (null)}
2021-04-23 18:11:21.775718+0800 IQHIPan[15592:324628] b3 <NSThread: 0x600003d1c200>{number = 5, name = (null)}
总结
在没有Serial Dispatch Queue 和dispatch_barrier_async 函数那么大粒度且一部分处理需要进行排他控制情况下,dispatch_semaphore便可以发挥重大作用。