聊一聊你理解的多线程

•NSThread:
–优点:NSThread 比其他两个轻量级,使用简单
–缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销

[objc]  view plain  copy
  1. //创建线程方法1  
  2.     NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadAction:) object:nil];    
  3.     [thread start];//开启子线程   
  4.     [thread cancel];//取消子线程  
  5.      //创建线程方法2-立即在线程中执行任务  
  6.     [NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:nil];    
  7.     //创建线程方法3-在后台子线程中执行任务  
  8.     [self performSelectorInBackground:@selector(threadAction:) withObject:nil];  
  9.       
  10. -(void)threadAction:(id*)sender{   
  11.     @autoreleasepool {  
  12.         //子线程中通知主线程通常使用以下两种办法  
  13.         // [self.imageview performSelectorOnMainThread:@selector(updateView:) withObject:nil waitUntilDone:YES];  
  14.           
  15.         // [self.imageview performSelector:@selector(updateView:) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];  
  16.     }  
线程间通讯

线程下载完图片后怎么通知主线程更新界面呢?

performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的

两种锁,一种NSCondition ,一种是:NSLock

 // 锁对象   
    theLock = [[NSLock alloc] init];   
    ticketsCondition = [[NSCondition alloc] init];
        // 上锁   
//      [ticketsCondition lock];   
        [theLock lock];   
//中间写代码

  //开锁
//      [ticketsCondition unlock]; 
        [theLock unlock]; 


可以通过[ticketsCondition signal]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。
NSCondition的wait其实就是在线程内等待一个信号量, 信号量出现时就继续, 否则一直等下去
也可以用- (BOOL)waitUntilDate:(NSDate *)limit; 
这个在给定的时间到达时仍未有信号量出现, 就自动继续了.
如果用户给出信号量来触发继续的话, 会返回1
如果超时触发继续, 返回0
theLock = [[NSLock alloc] init];   
    // 锁对象   
    ticketsCondition = [[NSCondition alloc] init];   

        [ticketsCondition lock];   
        [NSThread sleepForTimeInterval:3];   
        [ticketsCondition signal];   //唤醒另一个线程
        [ticketsCondition unlock];   

        // 上锁   
        [ticketsCondition lock];   
        [ticketsCondition wait];   

        [theLock lock];   
     //要做的事情
        [theLock unlock];   

        [ticketsCondition unlock];  

其他同步
我们可以使用指令 @synchronized 来简化 NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。

    @synchronized(anObj) 
    { 
        // Everything between the braces is protected by the @synchronized directive. 
    } 
还有其他的一些锁对象,
比如:循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock等等,可以自己看官方文档学习


•NSOperation:
–不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上
–NSOperation是面向对象的


  //NSOperationQueue
     //两种操作-(操作本身跟多线程关系不大)
     //NSInvocationOperation
     //NSBlockOperation
    NSInvocationOperation *inop = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(inopAction:) object:nil];
    //[inop start];
    NSBlockOperation *blop = [NSBlockOperation blockOperationWithBlock:^{
        @autoreleasepool { NSLog(@"blop"); }   }];
    
    //队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //1的话顺序执行
    //同时执行最大操作数
    queue.maxConcurrentOperationCount = 3;
    //依赖关系
    [inop addDependency:blop];//blop执行完,才能执行inop
    //向队列添加操作
    [queue addOperation:inop];
    [queue addOperation:blop];

-(void)inopAction:(id)sender{
    @autoreleasepool { NSLog(@"inop"); }
    }
   

关于并发数
(1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3
(2)最大并发数:同一时间最多只能执行的任务的个数。
(3)最⼤大并发数的相关⽅方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 
说明:如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能内存多久开多一点,内存少就开少一点。
注意:num的值并不代表线程的个数,仅仅代表线程的ID。
提示:最大并发数不要乱写(5以内),不要开太多,一般以2~3为宜,因为虽然任务是在子线程进行处理的,但是cpu处理这些过多的子线程可能会影响UI,让UI变卡。


•GCD:

–Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术
–GCD是基于C语言的

GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。
一个任务可以是一个函数(function)或者是一个block。 GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。
GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行
dispatch queue分为下面三种:
Serial     
又称为private dispatch queues,同时只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。
Concurrent
又称为global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的。
Main dispatch queue
它是全局可用的serial queue,它是在应用程序主线程上执行任务的。



The main queue: 与主线程功能相同。实际上,提交⾄至main queue的任务会在主线程中执⾏行。main queue可以调⽤用dispatch_get_main_queue()来获得。因为mainqueue是与主线程相关的,所以这是⼀一个串⾏行队列。
Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:⾼高、中(默认)、低、后台四个优先级队列。可以调⽤用dispatch_get_global_queue函数传⼊入优先级来访问队列。优先级:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN



dispatch_sync(),同步添加操作。等待添加进队列里面的操作完成之后再继续执行。调用以后等到block执行完以后才返回 ,dispatch_sync()会阻塞当前线程。
dispatch_async ,异步添加进任务队列,调用以后立即返回,它不会做任何等待


在多线程开发当中,程序员只要将想做的事情定义好,并追加到DispatchQueue(派发队列)当中就好了。
  派发队列分为两种,一种是串行队列(SerialDispatchQueue),一种是并行队列(ConcurrentDispatchQueue)。
  一个任务就是一个block,比如,将任务添加到队列中的代码是:
  1 dispatch_async(queue, block);
  当给queue添加多个任务时,如果queue是串行队列,则它们按顺序一个个执行,同时处理的任务只有一个。
  当queue是并行队列时,不论第一个任务是否结束,都会立刻开始执行后面的任务,也就是可以同时执行多个任务。
  但是并行执行的任务数量取决于XNU内核,是不可控的。比如,如果同时执行10个任务,那么10个任务并不是开启10个线程,线程会根据任务执行情况复用,由系统控制。


延时的实现

[objc]  view plain  copy
  1.  //第一种NSThread延时  
  2.     [NSThread sleepForTimeInterval:3];//延时3秒-阻塞主线程  
  3.     //第二种  
  4.     [self performSelector:@selector(dosth) withObject:nil afterDelay:2];//延时3秒执行,不会阻塞主线程  
  5.       
  6.     //第三种GCD 3秒回到主线程执行 不会阻塞主线程  
  7.     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(33 * NSEC_PER_SEC)),  
  8.                      
  9.                    dispatch_get_main_queue(), ^{NSLog(@"第三种");} );  
  10.       
  11.     //第四种 GCD  
  12.     dispatch_queue_t qq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  13.     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)4*NSEC_PER_SEC), qq, ^{  
  14.         NSLog(@"第四种");  
  15.     });  
  16.       
  17. -(void)dosth{  
  18.     NSLog(@"第二种");  
  19. }  
dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行


dispatch_group_async的使用
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。


[objc]  view plain  copy
  1. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  2. dispatch_group_t group = dispatch_group_create();  
  3. dispatch_group_async(group, queue, ^{  
  4.     [NSThread sleepForTimeInterval:1];  
  5.     NSLog(@"group1");  
  6. });  
  7. dispatch_group_async(group, queue, ^{  
  8.     [NSThread sleepForTimeInterval:2];  
  9.     NSLog(@"group2");  
  10. });  
  11. dispatch_group_async(group, queue, ^{  
  12.     [NSThread sleepForTimeInterval:3];  
  13.     NSLog(@"group3");  
  14. });  
  15. dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
  16.     NSLog(@"updateUi");  
  17. });  
  18. dispatch_release(group);  


[objc]  view plain  copy
  1. //重复执行  
  2.   //放到全局队列才执行  
  3.   dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t t) {  
  4.       NSLog(@"重复执行,%ld",t);  
  5.   });  


Operation、GCD对比:

优点:不需要关心线程管理,数据同步的事情。
两者区别:
NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。
GCD主要与block结合使用。代码简洁高效

1. 性能:GCD更接近底层,而NSOperationQueue则更高级抽象,所以GCD在追求性能的底层操作来说,是速度最快的。这取决于使用Instruments进行代码性能分析,如有必要的话

2. 从异步操作之间的事务性,顺序行,依赖关系。GCD需要自己写更多的代码来实现,而NSOperationQueue已经内建了这些支持

3. 如果异步操作的过程需要更多的被交互和UI呈现出来,NSOperationQueue会是一个更好的选择。底层代码中,任务之间不太互相依赖,而需要更高的并发能力,GCD则更有优势

GCD

Grand Central Dispatch (GCD)Apple 开发的一个多核编程的解决方法。该方法在 Mac OS X 10.6 雪豹中首次推出,并随后被引入到了 iOS4.0中。GCD 是一个替代诸如 NSThread, NSOperationQueue, NSInvocationOperation 等技术的很高效和强大的技术。
GCDblock的配合使用,可以方便地进行多线程编程。

任务和队列

1.任务分为同步任务和异步任务,队列分为串行和并行
2.同步任务不会开线程,异步任务会开线程


- (void)sync_queue:(dispatch_queue_t)queue{

    //同步任务
    dispatch_sync(queue, ^{
        NSLog(@"同步1 - %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"同步2 - %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"同步3 - %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"同步4 - %@",[NSThread currentThread]);
    });
}

- (void)async_queue:(dispatch_queue_t)queue{
    
    //异步任务
    dispatch_async(queue, ^{
        NSLog(@"异步1 - %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"异步2 - %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"异步3 - %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"异步4 - %@",[NSThread currentThread]);
    });
}

3.串行队列一个一个的执行,并行队列一起执行

同步任务

dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)

异步任务

dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)

主队列

dispatch_queue_t mainQueue = dispatch_get_main_queue();

全局并发队列

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

自定义串行队列

dispatch_queue_t serialQueue = dispatch_queue_create("wanglei", NULL);

自定义并行队列

dispatch_queue_t concurrentQueue = dispatch_queue_create("king", DISPATCH_QUEUE_CONCURRENT);

同步主线程会卡死,造成死循环

[self sync_queue:mainQueue];

异步主线程不会开线程,顺序执行

[self async_queue:mainQueue];

同步全局并发不会开线程,顺序执行

[self sync_queue:globalQueue];

异步全局并发会开线程,乱序执行

[self async_queue:globalQueue];

同步串行不会开线程,顺序执行

[self sync_queue:serialQueue];

异步串行会开线程,顺序执行

[self async_queue:serialQueue];

同步并行不会开线程,顺序执行

[self sync_queue:concurrentQueue];

异步并行会开线程,乱序执行

[self async_queue:concurrentQueue];


线程间通信

#pragma mark - 线程间通讯
- (void)threadCommunication{

    //主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //全局并发队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(globalQueue, ^{
        
        NSURL *url = [NSURL URLWithString:@""];
        NSData *data = [[NSData alloc]initWithContentsOfURL:url];
        NSLog(@"%@",[NSThread currentThread]);
        
        dispatch_async(mainQueue, ^{
            
            data;
            //在这里刷新UI
            NSLog(@"mainQueue -- %@",[NSThread currentThread]);
        });
    });
}

GCD创建单例

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"只执行一次");
    });
}

GCD创建分组任务

- (void)dispatch_group{

    // 创建一个分组
    dispatch_group_t group = dispatch_group_create();
    
    // 全局队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 第一个参数: 任务所在的分组
    // 第二个参数: 任务所在的队列
    
    dispatch_group_async(group, globalQueue, ^{
        NSLog(@"分组任务1");
    });
    
    dispatch_group_async(group, globalQueue, ^{
        NSLog(@"分组任务2");
    });
    
    // 当上面两个任务都完成以后,会执行这个方法,我们在这里处理我们的需求
    dispatch_group_notify(group, globalQueue, ^{
        NSLog(@"上面分组任务完成后,才会执行");
    });
}

GCD延迟操作

- (void)dosomethingByTime{

    // 延迟加载函数
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延迟10s加载");
    });
    
    // 自定义并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("wanglei", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"dispatch_async - 1-%@",[NSThread currentThread]);
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"dispatch_async - 2-%@",[NSThread currentThread]);
    });
    
    // dispatch_barrier_async 使用于并行环境下
    // 使用dispatch_barrier_async添加的任务会在之前的block全部运行完毕之后,才会继续执行。保证对非线程安全的对象进行正确的操作
    // 运行完dispatch_barrier_async的block才会执行后面的任务
    // dispatch_barrier_async所在的线程跟前一个任务是同一条线程
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"dispatch_barrier_async-%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"dispatch_barrier_async- 3 -%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"dispatch_barrier_async- 4 -%@",[NSThread currentThread]);
    });

}

GCD下载图片及合成

- (void)drawRectImage{

    // 创建全局并发队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 异步下载
    dispatch_async(globalQueue, ^{
        
        // 下载第一张图片
        NSURL *url1 = [NSURL URLWithString:@"http://pic6.huitu.com/res/20130116/84481_20130116142820494200_1.jpg"];
        NSData *data1 = [NSData dataWithContentsOfURL:url1];
        UIImage *image1 = [UIImage imageWithData:data1];
        
        // 下载第二张图片
        NSURL *url2 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/c2cec3fdfc03924578c6cfe18394a4c27c1e25e8.jpg"];
        NSData *data2 = [NSData dataWithContentsOfURL:url2];
        UIImage *image2 = [UIImage imageWithData:data2];
        
        // 合并图片
        // 开启一个位图上下文
        UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
        
        // 绘制第一张图片
        CGFloat image1Width = image1.size.width;
        CGFloat image1Height = image1.size.height;
        [image1 drawInRect:CGRectMake(0, 0, image1Width, image1Height)];
        
        // 绘制第二张图片
        CGFloat image2Width = image2.size.width * 0.5;
        CGFloat image2Height = image2.size.height * 0.5;
        CGFloat image2Y = image1Height - image2Height;
        [image2 drawInRect:CGRectMake(0, image2Y, image2Width, image2Height)];
        
        // 得到上下文中的图片
        UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
        
        // 结束上下文
        UIGraphicsEndImageContext();
        
        // 回到主线程显示图片
        
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        dispatch_async(mainQueue, ^{
            self.imageView.image = fullImage;
        });
    });
}


dispatch_source_t

[objc]  view plain  copy
  1. //dispatch_source_t 默认是挂起的,需要dispatch_resume()  
  2. //这个和子线程处理数据主线程更新界面的优点在于,当主线程比较空闲一起更新界面.效率更高  
  3. dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 00, dispatch_get_main_queue());  
  4.   
  5. dispatch_source_set_event_handler(source, ^{  
  6.     NSLog(@"%ld",dispatch_source_get_data(source));  
  7. });  
  8.   
  9. dispatch_resume(source);  
  10.   
  11. dispatch_apply(100, dispatch_get_global_queue(00), ^(size_t index) {  
  12.     // do some work on data at index  
  13.     dispatch_source_merge_data(source, 1);  
  14. }); 

GCD实现的验证码倒计时

- (void)btnClick{
     [_btn setTitle:@"重发(60s)" forState:UIControlStateNormal];
    __block int timeout=59; //倒计时时间
    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.btn.userInteractionEnabled = YES;
                [self.btn setTitle:@"获取验证码" forState:UIControlStateNormal];
                
            });
        }else{
            int seconds = timeout % 60;
            NSString *strTime = [NSString stringWithFormat:@"%.2d", seconds];
            dispatch_async(dispatch_get_main_queue(), ^{
                //设置界面的按钮显示 根据自己需求设置
                [UIView beginAnimations:nil context:nil];
                [UIView setAnimationDuration:1];
                [self.btn setTitle:[NSString stringWithFormat:@"重发(%@秒)",strTime] forState:UIControlStateNormal];
                
                [UIView commitAnimations];
                self.btn.userInteractionEnabled = NO;
                
            });
            timeout--;
        }
    });
    
    dispatch_resume(_timer);
    



}


dispatch_barrier_async

[objc]  view plain  copy
  1. //    作用是在并行队列中,等待前面两个操作并行操作完成,再执行后面的输出  
  2.     dispatch_queue_t queue6 =dispatch_queue_create("gcdtest.zfl.demo",DISPATCH_QUEUE_CONCURRENT);  
  3.       
  4.     dispatch_async(queue6, ^{  
  5.           
  6.         [NSThread sleepForTimeInterval:2];  
  7.           
  8.         NSLog(@"dispatch_async1");  
  9.           
  10.     });  
  11.       
  12.     dispatch_async(queue6, ^{  
  13.           
  14.         NSLog(@"dispatch_async2");  
  15.           
  16.     });  
  17.       
  18.     dispatch_barrier_async(queue6, ^{  
  19.           
  20.         NSLog(@"dispatch_barrier_async");  
  21.           
  22.     });  
  23.       
  24.     dispatch_async(queue6, ^{  
  25.           
  26.         NSLog(@"dispatch_async3");  
  27.           


信号量概述(引用百度百科):

  以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看 门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开 车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

  抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。

 

Demo解析 

    //    创建一个信号量,值为0        
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        //    在一个操作结束后发信号,这会使得信号量+1
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {

            dispatch_semaphore_signal(sema);

        });
    //    一开始执行到这里信号量为0,线程被阻塞,直到上述操作完成使信号量+1,线程解除阻塞
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
 
  
    //    创建一个组 
    dispatch_group_t group = dispatch_group_create();
    //    创建信号 信号量为10
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    //    取得默认的全局并发队列
    dispatch_queue_t queue =    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for(inti = 0; i < 100; i++)
    {
        //    由于信号量为10 队列里面最多会有10个人任务被执行,
       dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
        //    任务加到组内被监听
        dispatch_group_async(group, queue, ^{
            NSLog(@"%i",i);
            sleep(2);
            dispatch_semaphore_signal(semaphore);
        });
    }
    //    等待组内所有任务完成,否则阻塞
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);
    dispatch_release(semaphore);   


猜你喜欢

转载自blog.csdn.net/bug_delete/article/details/79569413