#iOS简单优雅的实现复杂状况下的串行需求(各类锁、GCD 、NSOperationQueue...)git
昨天一个同事问我一个问题,我在开发中有不少异步操做,回调都须要时间,且时间都不肯定,例如一个网络请求,就是这样的形式,异步发起请求,等待回调,等到获取结果以后进行下一步的操做. 我说,没有任何问题啊.原本耗时操做等就是这么写的啊... 而后他说,我如今有一个新的需求,例如网络请求1结束后请求2等到2回来以后再请求3....层层下去...按照顺序来,我说这个需求不算太难. 可是鉴于这个需求不少人都有可能会用到,因而我打算把它给写下来分享给你们github
每一次的异步操做大概能够简化成以下:bash
-(void)doSomeThingForFlag:(NSInteger)flag finish:(void(^)())finshed{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"do:%ld",(long)flag);
sleep(2+arc4random_uniform(4));
NSLog(@"finish:%ld",(long)flag);
if (finshed) {
finshed();
}
});
}
复制代码
那么正常状况下的写法就是直接按照顺序来网络
-(void)nomal{
[self doSomeThingForFlag:1 finish:nil];
[self doSomeThingForFlag:2 finish:nil];
[self doSomeThingForFlag:3 finish:nil];
[self doSomeThingForFlag:4 finish:nil];
}
复制代码
那么这样有效果么?咱们运行看看... 多线程
没有办法,那么使用嵌套?就是最普通的方式看看,并发
/**
逻辑嵌套
*/
-(void)useNested{
__weak typeof(self)weakSelf = self;
[self doSomeThingForFlag:1 finish:^{
[weakSelf doSomeThingForFlag:2 finish:^{
[weakSelf doSomeThingForFlag:3 finish:^{
[weakSelf doSomeThingForFlag:4 finish:nil];
}];
}];
}];
}
复制代码
OK ,结果彻底按照想要的顺序1->2->3->4 可是这样写会不会就以为很嵌套的太多了呢?有没有办法不使用这种嵌套来完成这个逻辑呢? 开始构思,首先想到的就是锁,是的,应用开发中有不少所可以完成 iOS做为源自C的更高级语言,天然而然也少不了有各类锁的实现.包含C语言的话, 有OSSpinLock、pthead、@synchronized、NSLock......大概七、8种以上吧.. 在不考虑各类锁的性能的状况下,那么是否是全部的都特别适用呢? 我一个一个举例尝试,大体的思路就是建立一个锁,而后经过加锁和解锁的操做来实现串行的需求 首先是使用dom
pthread_mutex 互斥锁异步
#import <pthread.h>
/**
pthread_mutex 互斥锁
*/
-(void)usePthred{
static pthread_mutex_t pLock;
pthread_mutex_init(&pLock, NULL);
pthread_mutex_lock(&pLock);
NSLog(@"1上锁");
[self doSomeThingForFlag:1 finish:^{
NSLog(@"1解锁");
pthread_mutex_unlock(&pLock);
}];
pthread_mutex_lock(&pLock);
NSLog(@"2上锁");
[self doSomeThingForFlag:2 finish:^{
NSLog(@"2解锁");
pthread_mutex_unlock(&pLock);
}];
pthread_mutex_lock(&pLock);
NSLog(@"3上锁");
[self doSomeThingForFlag:3 finish:^{
NSLog(@"3解锁");
pthread_mutex_unlock(&pLock);
}];
pthread_mutex_lock(&pLock);
NSLog(@"4上锁");
[self doSomeThingForFlag:4 finish:^{
NSLog(@"4解锁");
pthread_mutex_unlock(&pLock);
}];
}
复制代码
好吧,轻易的实现了 async
那么既然互斥锁能够,我再试试另外一种pthead性能
pthread_mutex(recursive) 递归锁
/**
pthread_mutex(recursive) 递归锁
*/
-(void)usePthredResursive{
static pthread_mutex_t pLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); //初始化attr而且给它赋予默认
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //设置锁类型,这边是设置为递归锁
pthread_mutex_init(&pLock, &attr);
pthread_mutexattr_destroy(&attr); //销毁一个属性对象,在从新进行初始化以前该结构不能从新使用
static void (^RecursiveBlock)(int);
__weak typeof(self)weakSelf = self;
RecursiveBlock = ^(int value) {
pthread_mutex_lock(&pLock);
if (value>0) {
[weakSelf doSomeThingForFlag:5-value finish:^{
RecursiveBlock(value-1);
}];
}
// if (value == 4) {
// [self doSomeThingForFlag:1 finish:^{
// RecursiveBlock(3);
// }];
// }
// if (value == 3) {
// [self doSomeThingForFlag:2 finish:^{
// RecursiveBlock(2);
// }];
// }
// if (value == 2) {
// [self doSomeThingForFlag:3 finish:^{
// RecursiveBlock(1);
// }];
// }
// if (value == 1) {
// [self doSomeThingForFlag:4 finish:^{
// RecursiveBlock(0);
// }];
// }
pthread_mutex_unlock(&pLock);
};
RecursiveBlock(4);
}
复制代码
递归锁容许同一个线程在未释放其拥有的锁时反复对该锁进行加锁操做。 结果也是能够可以实现的
表面上看感受递归锁貌似是没有问题的 可是其实在这里锁并无起到做用,这里的锁只是锁住了doSomeThingForFlag:finish:
这个方法而已 其实咱们把这些所有去掉看看.
-(void)usePthredResursive{
// static pthread_mutex_t pLock;
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr); //初始化attr而且给它赋予默认
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //设置锁类型,这边是设置为递归锁
// pthread_mutex_init(&pLock, &attr);
// pthread_mutexattr_destroy(&attr); //销毁一个属性对象,在从新进行初始化以前该结构不能从新使用
static void (^RecursiveBlock)(int);
__weak typeof(self)weakSelf = self;
RecursiveBlock = ^(int value) {
// pthread_mutex_lock(&pLock);
if (value>0) {
[weakSelf doSomeThingForFlag:5-value finish:^{
RecursiveBlock(value-1);
}];
}
// pthread_mutex_unlock(&pLock);
};
RecursiveBlock(4);
}
复制代码
结果也是同样?是的,没有想的那么高深,他只是上面嵌套的另外一种写法而已,因此递归锁并无效果,它只是锁住方法自己,保证一次只有一个执行而已,若是咱们把block的调用放到方法的外面同样没有做用
-(void)usePthredResursive{
static pthread_mutex_t pLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); //初始化attr而且给它赋予默认
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //设置锁类型,这边是设置为递归锁
pthread_mutex_init(&pLock, &attr);
pthread_mutexattr_destroy(&attr); //销毁一个属性对象,在从新进行初始化以前该结构不能从新使用
static void (^RecursiveBlock)(int);
__weak typeof(self)weakSelf = self;
RecursiveBlock = ^(int value) {
pthread_mutex_lock(&pLock);
if (value>0) {
[weakSelf doSomeThingForFlag:5-value finish:nil];
RecursiveBlock(value-1);
}
pthread_mutex_unlock(&pLock);
};
RecursiveBlock(4);
}
复制代码
这样的话就是没有达到需求
因此我打算放弃递归锁的实现,好比NSRecursiveLock,我直接放弃 联想到这样的方式,我打算再次放弃另一种锁@synchronized 由于它也只能锁住方法的自己,并控制不了回调的结果
那么就么有方法了么?只能使用递归或者嵌套 或者互斥锁么?
C的方法我又想到了自旋锁
OSSpinLock 自旋锁
#import <libkern/OSAtomic.h>
/**
OSSpinLock 自旋锁
*/
-(void)useOSSpinLock{
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
OSSpinLockLock(&oslock);
NSLog(@"1上锁");
[self doSomeThingForFlag:1 finish:^{
NSLog(@"1解锁");
OSSpinLockUnlock(&oslock);
}];
OSSpinLockLock(&oslock);
NSLog(@"2上锁");
[self doSomeThingForFlag:2 finish:^{
NSLog(@"2解锁");
OSSpinLockUnlock(&oslock);
}];
OSSpinLockLock(&oslock);
NSLog(@"3上锁");
[self doSomeThingForFlag:3 finish:^{
NSLog(@"3解锁");
OSSpinLockUnlock(&oslock);
}];
OSSpinLockLock(&oslock);
NSLog(@"4上锁");
[self doSomeThingForFlag:4 finish:^{
NSLog(@"4解锁");
OSSpinLockUnlock(&oslock);
}];
}
复制代码
结果终于回到了想要的局面,OSSpinLock没有让我失望.
至此我就想到了,无上面写的方法基本上就是使用各类锁的实现,来达到需求,在结果回调前把线程给锁住,没法继续新的线程,知道该线程的锁解开
那么咱们能不能使用多线程的某些方法来实现呢?好比阻塞线程,好比线程的暂停和恢复 首先想到的就是GCD 相似于OSSpinLock, 咱们尝试使用GCD的信号量看看能不可以实现
dispatch_semaphore_t
/**
GCD single
*/
-(void)useGCDSingle{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"1阻塞线程");
[self doSomeThingForFlag:1 finish:^{
NSLog(@"1释放线程");
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"2阻塞线程");
[self doSomeThingForFlag:2 finish:^{
NSLog(@"2释放线程");
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"3阻塞线程");
[self doSomeThingForFlag:3 finish:^ {
NSLog(@"3释放线程");
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"4阻塞线程");
[self doSomeThingForFlag:4 finish:^{
NSLog(@"4释放线程");
dispatch_semaphore_signal(semaphore);
}];
}
复制代码
或者使用
dispatch_suspend、dispatch_resume
这里须要注意一些东西
/**
GCD队列的暂停和恢复
*/
-(void)useGCDSuspendAndResume{
dispatch_queue_t myqueue = dispatch_queue_create("com.charles.queue", NULL);
dispatch_async(myqueue, ^{
dispatch_suspend(myqueue);
[self doSomeThingForFlag:1 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
dispatch_suspend(myqueue);
[self doSomeThingForFlag:2 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
dispatch_suspend(myqueue);
[self doSomeThingForFlag:3 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
dispatch_suspend(myqueue);
[self doSomeThingForFlag:4 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
});
}
复制代码
无效!我是哪里想错了么? 暂停恢复,想想,串行队列嘛,固然要串行的添加啦,
/**
GCD队列的暂停和恢复
*/
-(void)useGCDSuspendAndResume{
dispatch_queue_t myqueue = dispatch_queue_create("com.charles.queue", NULL);
dispatch_async(myqueue, ^{
dispatch_suspend(myqueue);
[self doSomeThingForFlag:1 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
});
dispatch_async(myqueue, ^{
dispatch_suspend(myqueue);
[self doSomeThingForFlag:2 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
});
dispatch_async(myqueue, ^{
dispatch_suspend(myqueue);
[self doSomeThingForFlag:3 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
});
dispatch_async(myqueue, ^{
dispatch_suspend(myqueue);
[self doSomeThingForFlag:4 finish:^(NSInteger flag) {
dispatch_resume(myqueue);
}];
});
}
复制代码
果真,开始是我想错了....
那么既然GCD能够,我使用NSOperationQueue呢?
NSOperationQueue
/**
operationQueue的暂停和恢复
*/
-(void)useOperationQueue{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
__weak typeof(self)weakSelf = self;
NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{
[queue setSuspended:YES];
[weakSelf doSomeThingForFlag:1 finish:^(NSInteger flag) {
[queue setSuspended:NO];
}];
}];
NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{
[queue setSuspended:YES];
[weakSelf doSomeThingForFlag:2 finish:^(NSInteger flag) {
[queue setSuspended:NO];
}];
}];
NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{
[queue setSuspended:YES];
[weakSelf doSomeThingForFlag:3 finish:^(NSInteger flag) {
[queue setSuspended:NO];
}];
}];
NSBlockOperation * operation4 = [NSBlockOperation blockOperationWithBlock:^{
[queue setSuspended:YES];
[weakSelf doSomeThingForFlag:4 finish:^(NSInteger flag) {
[queue setSuspended:NO];
}];
}];
[operation4 addDependency:operation3];
[operation3 addDependency:operation2];
[operation2 addDependency:operation1];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
}
复制代码
彻底参考GCD的思路,建立每个操做,添加依赖和最大并发数保证大的串行,同是在没操做其中一个暂停队列,完成后恢复运行....
OK说了这么多,其实就是经过各类方式来实现我最上面提到的需求而已. 这样的操做真的有用么? 很碰巧,我最近在作蓝牙开发,有这样的相似需求,蓝牙发送指令并接收到设备端返回数据的状况就是一次相似的网络请求, 我碰到的需求是按顺序的设置指令到蓝牙设备端,若是是多个UUID或者characteristic的话,之间不冲突,没有影响,可是惋惜的是我要操做的是一个characteristic,我只能这么作,由于若是同一时间发送指令不是按照上面的逻辑的话,就会形成丢包.我可能发送了某一个指令,可是蓝牙没有收到或者未处理就来了新的指令致使我没法完整的操做它.我必须保证1->2->3->4的逻辑顺序,
我很高兴我正好在研究这个,因此我可以即时的给到我同事个人思路,,而且今天把它分享给大家 附上demo的地址 github.com/spicyShrimp…
还有我写的系列文章,刚刚开始写,但愿多关注一下. 系列:iOS开发-前言+大纲 blog.csdn.net/spicyShrimp…