ios 多线程控制线程并发数、GCD之dispatch_group、GCD信号量

来源:互联网 发布:shop域名有发展吗 编辑:程序博客网 时间:2024/03/29 16:08

=======GCD信号量=====

假设现在系统有两个空闲资源可以被利用,但同一时间却有三个线程要进行访问,这种情况下,该如何处理呢?

我们要下载很多图片,并发异步进行,每个下载都会开辟一个新线程,可是我们又担心太多线程肯定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{

    //cratevalue表示,最多几个资源可访问

    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_signalsemaphore的计数值加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的内部计数减一


原创粉丝点击