最近做一些設(shè)備指紋的收集工作缕棵,使用到信號量孵班,是為了解決異步API同步使用的問題。先來看需求:
陀螺儀加速計的數(shù)據(jù)收集招驴,主要有兩種方法篙程,一種是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];//開始更新陀螺儀數(shù)據(jù)
}
if ([self isGyroAvailable]){
data = self.gyroData.rotationRate;//獲取到陀螺儀數(shù)據(jù)
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), @"陀螺儀未啟動或者不可用虱饿,數(shù)據(jù)獲取失敗");
return data;
}
}
但是發(fā)現(xiàn),第一次獲取的數(shù)據(jù)總是0触趴。需要多次調(diào)用(一段時間間隔氮发,很小很小)之后才可以獲取到數(shù)據(jù)冗懦,這樣就造成了一個問題爽冕,我只想要調(diào)用方法就返回陀螺儀數(shù)據(jù),但是你給我返回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.
開始更新陀螺儀數(shù)據(jù),通過block在陀螺儀數(shù)據(jù)更新后回調(diào)返回數(shù)據(jù)扇售。
如此前塔,真正的需求出來了,如何在block回調(diào)之后承冰,return數(shù)據(jù)呢?歸類起來這是一個異步block變同步方法返回的需求食零,終于困乒,主角信號量要出現(xiàn)了!
先看GCD中的信號量API贰谣,非常簡單娜搂,只有三個方法
[圖片上傳失敗...(image-a87808-1538273800178)]
主要方法:
- 1.dispatch_semaphore_create //創(chuàng)建一個信號量semaphore
- 2.dispatch_semaphore_wait //信號量等待
- 3.dispatch_semaphore_signal //發(fā)送一個信號
信號量使用也很簡單:
- 信號量在創(chuàng)建的時候需要傳入一個long型value,內(nèi)部會保存這個value作為初始value吱抚,value 做加減變化后百宇,還會保存一份當(dāng)前的 value。
- 信號的 wait 和 signal 是互逆的兩個操作秘豹。signal 會將 value 加一携御。如果 value 大于 0,wait將 value 減一既绕;此時如果 value 小于零就一直等待啄刹。
- 初始 value 必須大于等于 0,如果為 0 并隨后調(diào)用 wait 方法凄贩,線程將被阻塞直到別的線程調(diào)用了 signal 方法誓军。
簡單了解了信號量的使用,異步block 同步返回的需求就很好解決了疲扎,結(jié)合信號量昵时,實現(xiàn)代碼如下
- (CMRotationRate)syncGetGyroData
{
__block CMRotationRate data;//保存block數(shù)據(jù)
data.x = 0.f;
data.y = 0.f;
data.z = 0.f;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);//創(chuàng)建信號量傳入 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回調(diào)的線程瓜挽,如果和syncGetGyroData方法的執(zhí)行線程相同的話盹廷,會造成線程死鎖,這里block回調(diào)不在主線程,所以可以這樣處理久橙,也算是一種小技巧.
另外同樣的需求俄占,也可以借助GCD dispatch_group實現(xiàn):
- (CMRotationRate)syncGetGyroDataByGroup
{
__block CMRotationRate data;//保存block數(shù)據(jù)
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;
}
信號量還可以用于控制線程并發(fā)數(shù)量,這里就不做詳細(xì)介紹了淆衷,可以參考
https://www.cnblogs.com/yajunLi/p/6274282.html
總結(jié):
這次需求缸榄,使用到了信號量。知其然不知其所以然祝拯,對于信號量的實現(xiàn)甚带,還不是很清楚她肯。工作中接觸新東西,很多情況都是這個過程鹰贵。所以下一步打算仔細(xì)了解一下信號量晴氨,GCD源碼,我來了碉输!