iOS信号量
信号量的本质:
信号量的本质是数据操作锁, 它本身不具有数据交换的功能,而是通过控制其他的通信资源来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能.
信号量的工作原理
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
举个例子,就是 两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为 当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
信号量的三个函数是:
dispatch_semaphore_create(long value); // 创建一个semaphore
dispatch_semaphore_signal(dispatch_semaphore_t dsema); // 发送一个信号
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); // 等到信号
举几个例子,代码如下:
-(void)dispatch_group_function1
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_queue_create("com.dispatch.test", DISPATCH_QUEUE_CONCURRENT), ^{
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 请求完成,可以通知界面刷新界面等操作
NSLog(@"第一步网络请求完成");
// 使信号的信号量+1,这里的信号量本来为0,+1信号量为1(绿灯)
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// 以下还要进行一些其他的耗时操作
NSLog(@"耗时操作1继续进行");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, dispatch_queue_create("com.dispatch.test", DISPATCH_QUEUE_CONCURRENT), ^{
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.github.com"]];
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 请求完成,可以通知界面刷新界面等操作
NSLog(@"第二步网络请求完成");
// 使信号的信号量+1,这里的信号量本来为0,+1信号量为1(绿灯)
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// 以下还要进行一些其他的耗时操作
NSLog(@"耗时操作2继续进行");
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新界面等在主线程的操作");
});
}
首先,默认初始值给的信号量值未0,所以线程1和线程2的执行上其实是没有顺序的,但是,由于在这里设置了dispatch_semaphore_wait,并且当前的信号量值未0,所以会堵塞线程,直到执行了dispatch_semaphore_signal信号量+1,才会继续执行之后的线程操作,打印结果为:
2019-09-27 15:02:51.420389+0800 CPTestDemo[5253:337990] 耗时操作2继续进行
2019-09-27 15:02:51.420414+0800 CPTestDemo[5253:337992] 耗时操作1继续进行
2019-09-27 15:02:51.544547+0800 CPTestDemo[5253:338000] 第一步网络请求完成
2019-09-27 15:02:53.100889+0800 CPTestDemo[5253:337995] 第二步网络请求完成
2019-09-27 15:02:53.101065+0800 CPTestDemo[5253:337950] 刷新界面等在主线程的操作
如果初始值设置dispatch_semaphore_create(1),又会是怎样的结果的
2019-09-27 15:05:34.816451+0800 CPTestDemo[5302:345432] 耗时操作1继续进行
2019-09-27 15:05:34.816451+0800 CPTestDemo[5302:345433] 耗时操作2继续进行
2019-09-27 15:05:34.926754+0800 CPTestDemo[5302:345437] 第一步网络请求完成
2019-09-27 15:05:34.926966+0800 CPTestDemo[5302:345393] 刷新界面等在主线程的操作
2019-09-27 15:05:36.475560+0800 CPTestDemo[5302:345437] 第二步网络请求完成
如果初始值设置dispatch_semaphore_create(2)呢
2019-09-27 15:27:59.737338+0800 CPTestDemo[5587:373223] 耗时操作1继续进行
2019-09-27 15:27:59.737338+0800 CPTestDemo[5587:373222] 耗时操作2继续进行
2019-09-27 15:27:59.758308+0800 CPTestDemo[5587:373190] 刷新界面等在主线程的操作
2019-09-27 15:27:59.880029+0800 CPTestDemo[5587:373220] 第一步网络请求完成
2019-09-27 15:28:01.847547+0800 CPTestDemo[5587:373220] 第二步网络请求完成
因为信号量初始值为2,线程1和2都会执行,由于执行完成信号量减为0,但是组中的任务已经执行完了,所以会接着执行dispatch_group_notify,然后依次是第一步网络请求和第二步网络请求