异步变同步,信号量的简单应用

2018-09-30  本文已影响39人  Boothlee

最近做一些设备指纹的收集工作,使用到信号量,是为了解决异步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)]

主要方法:

信号量使用也很简单:

简单了解了信号量的使用,异步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源码,我来了!

上一篇 下一篇

猜你喜欢

热点阅读