dispatch_semaphore GCD信号量

作者: 小胡子杰克 | 来源:发表于2016-12-02 15:44 被阅读277次
    • dispatch_semaphore 一共有三个方法

    dispatch_semaphore_create => 创建一个信号量
    
    dispatch_semaphore_signal => 发送一个信号
    
    dispatch_semaphore_wait => 等待信号
    

    关于信号量,一般可以用停车来比喻。
      停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。
      信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal 就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),
      调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;
      当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,所以就一直等下去。

    代码示例:

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    NSLog(@"等待semaphore");
    

    结果就是, 等待semaphore 这句话永远不会输出。原因有两个

    1. 你初始化信号量的时候,并没有库存,也就是你传入的值是0.
    2. 你传入等待增加库存的时间是 DISPATCH_TIME_FOREVER ,也就是说,除非有 地方运行了 dispatch_semaphore_signal 增加了库存,否则我永远等待下 去。 基于上述的两个原因,导致了程序不往下走了。

    • 信号量的应用

    1.测试异步网络请求

    在编写单元测试中就可以:

    - (void)downloadImageURLWithString:(NSString *)URLString
    {
    // 1
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0)
        NSURL *url = [NSURL URLWithString:URLString];
        __unused Photo *photo = [[Photo alloc] initwithURL:url withCompletionBlock:^(UIImage *image, NSError *error) {
          if (error) {
                XCTFail(@"%@ failed. %@", URLString, error);
            }
    // 2
          dispatch_semaphore_signal(semaphore);
        }];
    
    // 3
          dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, 5);
          if (dispatch_semaphore_wait(semaphore, timeoutTime)) {
              XCTFail(@"%@ timed out", URLString);
            }
    }
    

    2.控制并发量

    @implementation CustomOperationQueue
    
    - (id)initWithConcurrentCount:(int)count
    {
          self = [super init];
          if (self) {
            if (count < 1) count = 5;
            semaphore = dispatch_semaphore_create(count);
            queue = Dispatch_queue_create("Jake", DISPATCH_QUEUE_CONCURRENT);
          }
        return self;
    }
    
    - (id)init 
    {
          return [self initWithConcurrentCount:5];
    }
    
    - (void)addTask:(CallBackBlock)block
    {
        dispatch_async(queue, ^{
           dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
           dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 0), ^{
                block();
                dispatch_semaphore_signal(semaphore);
            });
        });
    }
    
    @end
    

    addTask:方法中
    1.当前的初始库存为5
    2.第一次调用dispatch_semaphore_wait的时候会直接执行下去,并减少一个库存。每当完成一个任务之后,会执行到dispatch_semaphore_signal将库存添加回去。
    3.当执行的任务为非常耗时的操作的时候,库存不能及时地还回去。而dispatch_semaphore_wait在仍然执行,库存最后会被减到0,这样dispatch_semaphore_wait就只能进行等待直到前面的任务有执行完成将存库有添加回去为止。

    如此便完成了并发量的控制!

    线程锁的使用:
    以下代码来自YYModel

    + (instancetype)classInfoWithClass:(Class)cls {
        if (!cls) return nil;
        static CFMutableDictionaryRef classCache;
        static CFMutableDictionaryRef metaCache;
        static dispatch_once_t onceToken;
        static dispatch_semaphore_t lock;
        dispatch_once(&onceToken, ^{
            classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
            metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
            lock = dispatch_semaphore_create(1);
        });
        dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
        YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
        if (info && info->_needUpdate) {
            [info _update];
        }
        dispatch_semaphore_signal(lock);
        if (!info) {
            info = [[YYClassInfo alloc] initWithClass:cls];
            if (info) {
                dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
                CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
                dispatch_semaphore_signal(lock);
            }
        }
        return info;
    }
    

    相关文章

      网友评论

        本文标题:dispatch_semaphore GCD信号量

        本文链接:https://www.haomeiwen.com/subject/pecqmttx.html