最近做一些设备指纹的收集工作,使用到信号量,是为了解决异步API同步使用的问题。先来看需求:
陀螺仪加速计的数据收集,主要有两种方法,一种是pull方法,如下:
- (CMRotationRate)getGyroData
{
CMRotationRate data;
data.x = 0.f;
data.y = 0.f;
data.z = 0.f;
if ([self isGyroAvailable] && ![self isGyroActive]){//判断陀螺仪是否可用,是否活跃
self.gyroUpdateInterval = 0.01;
[self startGyroUpdates];//开始更新陀螺仪数据
}
if ([self isGyroAvailable]){
data = self.gyroData.rotationRate;//获取到陀螺仪数据
NSLog(@"X = %.04f",data.x);
NSLog(@"Y = %.04f",data.y);
NSLog(@"Z = %.04f",data.z);
return data;
}else{
NSAssert(!(data.x || data.y || data.z), @"陀螺仪未启动或者不可用,数据获取失败");
return data;
}
}
但是发现,第一次获取的数据总是0。需要多次调用(一段时间间隔,很小很小)之后才可以获取到数据,这样就造成了一个问题,我只想要调用方法就返回陀螺仪数据,但是你给我返回0.0000 WTF😂
那么,再来看陀螺仪的push方法:
- (void)startGyroUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMGyroHandler)handler API_UNAVAILABLE(tvos);
Discussion 是这样写的
Starts gyro updates, providing data to
the given handler through the given queue.
开始更新陀螺仪数据,通过block在陀螺仪数据更新后回调返回数据。
如此,真正的需求出来了,如何在block回调之后,return数据呢?归类起来这是一个异步block变同步方法返回的需求,终于,主角信号量要出现了!
先看GCD中的信号量API,非常简单,只有三个方法
[图片上传失败...(image-a87808-1538273800178)]
主要方法:
- 1.dispatch_semaphore_create //创建一个信号量semaphore
- 2.dispatch_semaphore_wait //信号量等待
- 3.dispatch_semaphore_signal //发送一个信号
信号量使用也很简单:
- 信号量在创建的时候需要传入一个long型value,内部会保存这个value作为初始value,value 做加减变化后,还会保存一份当前的 value。
- 信号的 wait 和 signal 是互逆的两个操作。signal 会将 value 加一。如果 value 大于 0,wait将 value 减一;此时如果 value 小于零就一直等待。
- 初始 value 必须大于等于 0,如果为 0 并随后调用 wait 方法,线程将被阻塞直到别的线程调用了 signal 方法。
简单了解了信号量的使用,异步block 同步返回的需求就很好解决了,结合信号量,实现代码如下
- (CMRotationRate)syncGetGyroData
{
__block CMRotationRate data;//保存block数据
data.x = 0.f;
data.y = 0.f;
data.z = 0.f;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);//创建信号量传入 0,此时遇如果wait会阻塞线程
[self startGyroUpdatesToQueue:[[NSOperationQueue alloc] init] withHandler:^(CMGyroData * _Nullable gyroData, NSError * _Nullable error) {
data = gyroData.rotationRate;
NSLog(@"X = %.04f",gyroData.rotationRate.x);
NSLog(@"Y = %.04f",gyroData.rotationRate.y);
NSLog(@"Z = %.04f",gyroData.rotationRate.z);
dispatch_semaphore_signal(sema);// 信号量 value 加一
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);//信号量为0时会阻塞线程,等待signle
return data;
}
以上就完美的解决了异步block同步返回的问题。信号量使用起来很简单。
但是需要注意
block回调的线程,如果和syncGetGyroData方法的执行线程相同的话,会造成线程死锁,这里block回调不在主线程,所以可以这样处理,也算是一种小技巧.
另外同样的需求,也可以借助GCD dispatch_group实现:
- (CMRotationRate)syncGetGyroDataByGroup
{
__block CMRotationRate data;//保存block数据
data.x = 0.f;
data.y = 0.f;
data.z = 0.f;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self startGyroUpdatesToQueue:[[NSOperationQueue alloc] init] withHandler:^(CMGyroData * _Nullable gyroData, NSError * _Nullable error) {
data = gyroData.rotationRate;
NSLog(@"X = %.04f",gyroData.rotationRate.x);
NSLog(@"Y = %.04f",gyroData.rotationRate.y);
NSLog(@"Z = %.04f",gyroData.rotationRate.z);
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return data;
}
信号量还可以用于控制线程并发数量,这里就不做详细介绍了,可以参考
https://www.cnblogs.com/yajunLi/p/6274282.html
总结:
这次需求,使用到了信号量。知其然不知其所以然,对于信号量的实现,还不是很清楚。工作中接触新东西,很多情况都是这个过程。所以下一步打算仔细了解一下信号量,GCD源码,我来了!