iOS 多线程之 dispatch_semaphore_t

3,978 阅读4分钟

信号量多用于线程同步,加锁等操作。先介绍它的几个函数

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); //创建信号量
 dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
  dispatch_semaphore_signal(semaphore);//释放信号量,使其加1

dispatch_semaphore_create 这个函数是用来创建信号量的,传参是个int,一般代表并发数。 为了更好的理解,我们打个比方,dispatch_semaphore_create(N)相当于创建了一个停车场,这个停车场一共有N个车位。dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);代表开进来一辆车(车位个数减一),如果车位个数减一后是负数,那么就会卡死在当前线程,直到DISPATCH_TIME_FOREVER,这个是超时时间,dispatch_semaphore_signal(semaphore) 代表有车开走了,车位加一。空说概念太抽象,我们结合一个例子。

- (void)calcuteAll {
    
    __block NSInteger sourceArrCount = 5;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); //创建信号量
 
    for (NSInteger i = 0; i < sourceArrCount; i++) {

        NSLog(@"for循环 i:%ld,sourceArrCount:%ld",i,sourceArrCount);

        dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);


        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"开始任务-线程:%@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:1];
            sourceArrCount--;
            NSLog(@"soucre的值 %ld",sourceArrCount);
            dispatch_semaphore_signal(semaphore);//释放信号量,使其加1
        });
        //当信号量的值为0时阻塞当前执行,直到值大于0时做减1操作,并执行后续代码
    }
    
    NSLog(@"end");

}

大家可以想一想,这个会如何打印?

2019-10-10 15:09:10.405877+0800 c==[8778:226108] for循环 i:0,sourceArrCount:5
2019-10-10 15:09:10.406000+0800 c==[8778:226108] for循环 i:1,sourceArrCount:5
2019-10-10 15:09:10.406055+0800 c==[8778:226167] 开始任务-线程:<NSThread: 0x600002075300>{number = 3, name = (null)}
2019-10-10 15:09:11.408821+0800 c==[8778:226167] soucre的值 4
2019-10-10 15:09:11.409116+0800 c==[8778:226108] for循环 i:2,sourceArrCount:4
2019-10-10 15:09:11.409163+0800 c==[8778:226167] 开始任务-线程:<NSThread: 0x600002075300>{number = 3, name = (null)}
2019-10-10 15:09:12.413658+0800 c==[8778:226167] soucre的值 3
2019-10-10 15:09:12.413930+0800 c==[8778:226108] end
2019-10-10 15:09:12.413971+0800 c==[8778:226167] 开始任务-线程:<NSThread: 0x600002075300>{number = 3, name = (null)}
2019-10-10 15:09:13.415371+0800 c==[8778:226167] soucre的值 2

来分析以下这个过程

 <1> for循环 i= 0:   sourceArrCount = 5,   
        执行 NSLog(@"for循环 i:%ld,sourceArrCount:%ld",i,sourceArrCount);                     
        信号量-1,为0,往下执行在子线程开辟一个异步任务A。
 
 <2> for循环 i= 1:   sourceArrCount = 5,
 
        执行 NSLog(@"for循环 i:%ld,sourceArrCount:%ld",i,sourceArrCount);   
        因为信号为0,无法再减一,卡死在主线程
            A任务完成,打印
            NSLog(@"开始任务-线程:%@",[NSThread currentThread]);
            NSLog(@"soucre的值 %ld",sourceArrCount);
            source为4, 
            同时信号量加1,为1,
        继续回到上一步,信号减一为0,往下执行,开辟一个异步任务B,
 <3> for循环 i= 2:   sourceArrCount = 4,
        执行 NSLog(@"for循环 i:%ld,sourceArrCount:%ld",i,sourceArrCount);
        因为信号量为0,又卡死在主线程,
        B任务完成,打印
            NSLog(@"开始任务-线程:%@",[NSThread currentThread]);
            NSLog(@"soucre的值 %ld",sourceArrCount);
            source为3,信号量为1,
        继续回到卡死的地方,信号减一为0,往下执行,开辟一个异步任务C,
<4> for循环 i= 3:   sourceArrCount = 3,循环结束
     打印2019-10-10 15:09:12.413930+0800 c==[8778:226108] end
     
      C任务完成。

信号量使用场景

 - (void)calcuteAll {
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:100];
    // 创建为1的信号量
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    for (int i = 0; i < 10000; i++) {
        dispatch_async(queue, ^{
            // 等待信号量
            //dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            [arrayM addObject:[NSNumber numberWithInt:i]];
//            [NSThread sleepForTimeInterval:.3];
            NSLog(@"%@--%@",[NSNumber numberWithInt:i],[NSThread currentThread]);
            // 发送信号量
           //dispatch_semaphore_signal(sem);
        });
    }

}

如果不加锁,多个线程同时往数组里加数据,会内存错乱,这里相当于给子线程加了个锁,它正在访问数组,其他线程访问不了。

再来介绍下dispatch_group,一共包含如下几个函数

//    dispatch_group_t group = dispatch_group_create();
//    dispatch_group_enter(group);//
//        dispatch_group_leave(group);//任务完成,加1

- (void)calcuteAll {
    
    __block NSInteger number = 0;

    dispatch_group_t group = dispatch_group_create();
     //A耗时操作
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(3);
        number += 2;
        NSLog(@"第0个回调");
        
    });
 
    //B网络请求
    dispatch_group_enter(group);
    [self sendRequestWithCompletion:^(id response) {
        number += [response integerValue];
        dispatch_group_leave(group);
        NSLog(@"第一个回调");
    }sleepTime:5];
    
    //C网络请求
    dispatch_group_enter(group);
    [self sendRequestWithCompletion:^(id response) {
        dispatch_group_leave(group);
        number += [response integerValue];
        NSLog(@"第二个回调");
        
    }sleepTime:2] ;
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"%zd", number);
    });

}

dispatch_group_enter 和dispatch_group_leave 成对出现,dispatch_group_leave多的话,会crash。