dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
=======GCD信号量=====
https://www.jianshu.com/p/8fb33b89b5a7
1.dispatch_semaphore_create
创建一个信号量的初始值传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。
值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。
2.dispatch_semaphore_signal
发送一个信号这个函数会使传入的信号量dsema的值加1。
返回值为long类型,当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。
当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。
3.dispatch_semaphore_wait
等待一个信号这个函数会使传入的信号量dsema的值减1;
如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;
如果desema的值为0,那么这个函数就阻塞当前线程等timeout(注意timeout的类型为dispatch_time_t,不能直接传入整形或float数),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。
如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。
假设现在系统有两个空闲资源可以被利用,但同一时间却有三个线程要进行访问,这种情况下,该如何处理呢?
我们要下载很多图片,并发异步进行,每个下载都会开辟一个新线程,可是我们又担心太多线程肯定cpu吃不消,那么我们这里也可以用信号量控制一下最大开辟线程数。
定义:
1、信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。
其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
2、信号量主要有3个函数,分别是:
//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
dispatch_semaphore_wait 函数返回值为long类型;
当返回值为0时:说明semaphore的值大于等于1,或者在timeout指定时间之内,该函数所处的线程被成功唤醒(比如通过dispatch_semaphore_signal将线程唤醒)。
当返回值不为0时:说明semaphore的值等于0,此时timeout指定时间内该函数所处的线程处于阻塞。
另外,dispatch_semaphore_wait 函数的返回值也dispatch_group_wait 函数相同,可以通过返回值可以进行分支处理。
//增加信号量
dispatch_semaphore_signal(信号量)
当返回值为0时:表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。
当返回值不为0时:表示其当前有(一个或多个线程)等待其处理的信号量,并且该函数唤醒了一个“等待的线 程”(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)
注意,正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。
3、那么就开头提的问题,我们用代码来解决
-(void)dispatchSignal{
//crate的value表示,最多几个资源可访问
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});<br>
//任务2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});<br>
//任务3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
}
======GCD信号量测试2=====
信号量就是一个整数,并且具有一个初始计数值。支持两个操作:1信号通知,2等待。当一个信号量被通知 ,信号量就会加1,当一个信号等待,信号总量就减1,当减到信号量小于0时,线程会被阻塞,信号量不会在减了。直到信号量大于0时,线程会再次启动执行。
GCD信号量有三个函数:
dispatch_semaphore_create 创建一个信号量。
dispatch_semaphore_signal 发送一个信号
dispatch_semaphore_wait 等待信号。
直接上代码了,别的就不多说了,结合代码细细的讲解更有味道。
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRORITY_DEFAULT,0);
for(int i = 0;i<100;i++){
//等待信号量 如果信号总量大于0 那么每执行一次这个代码 信号量减少1 如果信号量小于0 那么执行这个代码的时候会阻塞线程。直到信号量再次大于0的时候在执行。
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
dispatch_group_async(group,queue,^{
NSLOG("nihao");
sleep(2);
//发送信号量,每执行一次信号量加1
dispatch_semaphore_signal(semaphore);
});
dispatch_group_wait(group,DISPATCH_TIME_FDREVER);
dispatch_release(group);
dispatch_release(semaphore);
}
这里我简单说一下代码:这里创建了一个初始值是10 的信号量,每一次for循环都会创建一个新的线程,并且for循环中的第一句代码就是信号量等待,每执行一次这句代码信号量都会减1,在后台执行块代码的时候,最后一句代码是发送信号量,这句代码又会让信号量加1,这样一个加 一个减,并且当信号量减少到小于0 的时候线程会被阻塞一直等待信号量,直到信号量大于0 的时候继续执行线程。这就保证了程序中并发执行的线程数量不会超过10.所以这样就很简单的控制了并发执行的线程数量。
=====GCD信号量=========
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore =dispatch_semaphore_create(1);
NSMutableArray *array = [[NSMutableArrayalloc]init];
for (NSInteger i = 0; i < 100000; i++) {
dispatch_async(queue, ^{
/*
此时semaphore信号量的值如果 >= 1时:对semaphore计数进行减1,然后dispatch_semaphore_wait函数返回。该函数所处线程就继续执行下面的语句。
此时semaphore信号量的值如果=0:那么就阻塞该函数所处的线程,阻塞时长为timeout指定的时间,如果阻塞时间内semaphore的值被dispatch_semaphore_signal函数加1了,该函数所处线程获得了信号量被唤醒。然后对semaphore计数进行减1并返回,继续向下执行。如果阻塞时间内没有获取到信号量唤醒线程或者信号量的值一直为0,那么就要等到指定的阻塞时间后,该函数所处线程才继续向下执行。
执行到这里semaphore的值总是1
*/
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
/* 因为dispatch_semaphore_create创建的semaphore的初始值为1,执行完上面的
dispatch_semaphore_wait函数之后,semaphore计数值减1会变为0,所以可访问array对象的线程只有1个,因此可安全地对array进行操作。
*/
[array addObject:[NSNumber numberWithInteger:i]];
/*
对array操作之后,通过dispatch_semaphore_signal将semaphore的计数值加1,此时semaphore的值由变成了1,所处
*/
dispatch_semaphore_signal(semaphore);
});
}
====GCD信号量测试4======
#import "ViewController.h"
typedef void(^FinishNetwork)();
@interface ViewController ()
@property (nonatomic,copy )FinishNetwork block;
@property (nonatomic,copy )NSString *string1;
@property (nonatomic,copy )NSString *string2;
@property (nonatomic,copy )NSString *string3;
@end
@implementation ViewController
- (void)viewDidLoad {
[superviewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[selfinitData];
}
- (void)initData {
// 创建信号量
dispatch_semaphore_t semaphore =dispatch_semaphore_create(0);
// 创建全局并行
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group =dispatch_group_create();
dispatch_group_async(group, queue, ^{
// 请求一
//这里通过 block表示请求结束,并标记一个信号量
[selfgetList1:^{
dispatch_semaphore_signal(semaphore);
}];
});
dispatch_group_async(group, queue, ^{
// 请求二
[selfgetList2:^{
dispatch_semaphore_signal(semaphore);
}];
});
dispatch_group_async(group, queue, ^{
// 请求三
[selfgetList3:^{
dispatch_semaphore_signal(semaphore);
}];
});
dispatch_group_notify(group, queue, ^{
//在这里进行请求后的方法
NSLog(@"string1:___%@",_string1);
NSLog(@"string2:___%@",_string2);
NSLog(@"string3:___%@",_string3);
// 三个请求对应三次信号等待
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
});
}
- (void)getList1:(FinishNetwork)block {
NSLog(@"加载列表1");
self.string1 =@"加载列表1";
}
- (void)getList2:(FinishNetwork)block {
NSLog(@"加载列表2");
self.string2 =@"加载列表2";
}
- (void)getList3:(FinishNetwork)block {
NSLog(@"加载列表3");
self.string3 =@"加载列表3";
}
- (void)didReceiveMemoryWarning {
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
参考:http://www.cnblogs.com/breezemist/p/5667776.html
dispatch_group_async,是用于同步工作的,但是,它的判断标准是放入的block是否执行完毕,如果我们放入block中包含异步的网络请求,这个方法无法在网络数据返回后再进行同步。
这是因为这里的网络请求是个异步的方法,没有等待具体的数据返回,放入的dispatch queue的 block就执行完毕了。所以没收到2个网络数据,就提前调用了dispatch_group_notify指定的结束方法。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURLSession *session = [NSURLSession sharedSession];
dispatch_queue_t dispatchQueue = dispatch_queue_create("test.queue",DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t dispatchGroup = dispatch_group_create();
// dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
dispatch_group_enter(dispatchGroup);
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
NSLog(@"got data from internet1");
dispatch_group_leave(dispatchGroup);
}];
[task resume];
// });
// dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
dispatch_group_enter(dispatchGroup);
NSURLSessionDataTask *task2 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
NSLog(@"got data from internet2");
dispatch_group_leave(dispatchGroup);
}];
[task2 resume];
// });
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"end");
});
}
看正确是输出结果:
2016-07-13 17:46:10.282 aaaa[4847:300370] got data from internet1
2016-07-13 17:46:10.501 aaaa[4847:300370] got data from internet2
2016-07-13 17:46:10.502 aaaa[4847:300341] end
dispatch_group_enter会对group的内部计数加一,dispatch_group_leave会对group的内部计数减一
====***************信号量使用
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_semaphore_t semaphore =dispatch_semaphore_create(1);//*******创建信号量
for(int i=0;i<2;i++){
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);//******减少信号量
if([SPMobileApplicationsharedInstance].isLogined){
if(i==0){
[self initWLBDebit:semaphore];}//必须在每个异步线程中增加信号量
else if(i==1){
[self initWLBCredit:semaphore];
}
}
});
}
=================
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);//*******创建信号量
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);//******减少信号量
[selfversionUpdateWithsingepal:semaphore];//必须在每个异步线程中增加信号量(dispatch_semaphore_signal(semaphore);)
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);//******减少信号量
[selfgetlunbobannerWithsingepal:semaphore];//获取轮播图,并且增加信号量//必须在每个异步线程中增加信号量(dispatch_semaphore_signal(semaphore);)
});
*************实现获取验证码的按钮倒计时
-(void)startTime:(NSInteger )timeout title:(NSString *)tittle waitTittle:(NSString *)waitTittle{
__block NSInteger timeOut=timeout; //倒计时时间
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
if(timeOut<=0){ //倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置
[self setTitle:tittle forState:UIControlStateNormal];
self.userInteractionEnabled = YES;
});
}else{
// int minutes = timeout / 60;
int seconds = timeOut % 60;
NSString *strTime = [NSString stringWithFormat:@"%.2d", seconds];
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置
NSLog(@"____%@",strTime);
[self setTitle:[NSString stringWithFormat:@"%@%@",strTime,waitTittle] forState:UIControlStateNormal];
self.userInteractionEnabled = NO;
});
timeOut--;
}
});
dispatch_resume(_timer);
}