iOS 多线程: 信号量

今天跟大家聊聊关于 GCD 中的信号量的使用.

网上有很多关于信号量的介绍, 这里只是结合本人一些理解和实际使用来分享给大家.

在GCD中有三个函数是semaphore的操作,分别是

1.创建一个semaphore 
dispatch_semaphore_create
2. 发送一个信号 
dispatch_semaphore_signal
会使信号量计数增加1.
3. 等待信号
dispatch_semaphore_wait
会使信号量计数减少1.

/usr/include/dispatch/semaphore.h 中关于函数的声明和定义, 注释很清晰.

这里特别需要注意的几个问题:

  1. 创建信号量的函数
    dispatch_semaphore_t dispatch_semaphore_create(long value);
    函数需要传入一个大于或者等于0的值, 传入小于0的值, 函数返回 NULL.
  2. 等待信号函数
    long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
    返回0表示成功, 非0表示超时, 会继续执行该函数后面的语句.
    关于参数 timeout, 后面细说.
  3. 发送信号函数
    long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
    返回非0值表示成功唤醒线程, 0表示没有唤醒.

下面结合实际应用场景, 说说信号量的使用和注意事项.

在开发过程中, 大家应该会有这样的需求

场景1:
等待上一个网络请求的返回结果, 再决定是否进行下一个网络请求.

示例代码:

dispatch_semaphore_t sema = dispatch_semaphore_create(0);  
[NetReq request:^(BOOL ok) {    
     // 唤醒等待的线程
   dispatch_semaphore_signal(sema);  
} onFail:^(int errorCode, NSString *errorMessage) {  
     // 唤醒等待的线程
   dispatch_semaphore_signal(sema);  
}];  

// 一直等待
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 

// 等待上面网络请求结束, 开始执行下面语句
[NetReq request...] 

场景2:
生产者和消费者问题

使用信号量可以解决该类型的问题.

示例代码

dispatch_semaphore_t signal = dispatch_semaphore_create(0);

if (NULL == signal) {
    return;
}

//消费者队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       while (true) {
            // 在超时时间内成功得到信号触发返回0值表示成功, 非0表示失败
            // 超时时间设置为DISPATCH_TIME_FOREVER, 表示一直等待, 其后语句被阻塞, 一直等到其被唤醒
            // DISPATCH_TIME_NOW, 表示不等待, 继续执行其后面的语句
            // 也可以指定时间, dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC)), 表示10s内等待, 超过10s, 继续执行下面语句
            long result = dispatch_semaphore_wait(signal, DISPATCH_TIME_NOW);

            // 超时了
            if (result != 0)  {
                continue;
            }

            // 被唤醒
            NSLog(@"1....消费 %@", self.lockData);

            [self.lockData removeObjectAtIndex:0];
        }
    });

    // 这里可以阻塞一段时间, 延时增加信号量来看看消费者队列中 wait 的情况
    sleep(5);

//生产者队列   
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (true) {
            [self.lockData addObject:@"0"];
            NSLog(@"0-------产出: %@", self.lockData);

            // 使信号量加1, 返回非0表示成功, 0表示失败
            long result = dispatch_semaphore_signal(signal);

            //失败
            if (0 == result) {
                //wait for a while
                sleep(1);
                continue;
            }

            //成功
            //TODO:
        }
    });

关于超时参数的定义在注释中有详细说明.

现在你可以动手试一试了.

©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页