对dispatch_semaphore_t的理解

- (void)semaphoreaTest {
    NSLog(@"semaphore---begin");
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    NSLog(@"semaphore---end");
}

关于dispatch_semaphore_t,网上的文章讲的都是

image.png

image.png

dispatch_semaphore_wait先会使semaphore count - 1,如果 - 1后的semaphore count >= 0,则放行,不然则阻塞当前线程,而dispatch_semaphore_signal只起到了semaphore count + 1的作用。

事实是这样吗,dispatch_semaphore_signal官方文档解释是:

Increment the counting semaphore. If the previous value was less than zero, this function wakes a thread currently waiting in dispatch_semaphore_wait

意思是dispatch_semaphore_signal会先让semaphore count + 1,如果+1前的semaphore count是 < 0的,则直接唤醒正在dispatch_semaphore_wait中等待的线程,所以我理解的是

dispatch_semaphore_wait只是起到了通过semaphore count判断是否需要阻塞当前线程并记录当前的线程的作用,当dispatch_semaphore_signal需要唤醒线程时,会根据dispatch_semaphore_wait记录的线程进行唤醒。

再加一个案例解释,火车票线程安全访问问题:

- (void)semaphoreThreadSafe {
    [self logCurrentThreadInfo:nil];
    NSLog(@"semaphore---begin");
    
    semaphore = dispatch_semaphore_create(1);
    ticketSurplusCount = 50;
    
    //queue1 1号窗口
    dispatch_queue_t queue1 = dispatch_queue_create("one.window.queue", DISPATCH_QUEUE_SERIAL);
    
    //queue2 2号窗口
    dispatch_queue_t queue2 = dispatch_queue_create("two.window.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue1, ^{
        [self saleTicketSafe];
    });
    dispatch_async(queue2, ^{
        [self saleTicketSafe];
    });
}

- (void)saleTicketSafe {
    while (1) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if (ticketSurplusCount > 0) {  // 如果还有票,继续售卖
            ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
            dispatch_semaphore_signal(semaphore);
        } else { // 如果已卖完,关闭售票窗口
            NSLog(@"所有火车票均已售完");
            
            // 相当于解锁
            dispatch_semaphore_signal(semaphore);
            break;
        }
    }
}

线程1:1号窗口所在的线程
线程2:2号窗口所在线程
semaphore count:信号量计数
线程1 访问 ticketSurplusCount,通过dispatch_semaphore_waitsemaphore count - 1,此时semaphore count = 0,不需要阻塞线程1
线程1 还没对ticketSurplusCount访问完成时,线程2 突然也要访问,经过dispatch_semaphore_wait时,semaphore count - 1,此时semaphore count = -1,需要阻塞 线程2线程2变为阻塞(等待)状态;
● 此时线程1ticketSurplusCount访问完成,调用dispatch_semaphore_signal使semaphore count + 1,现在的semaphore count = 0,然后由于semaphore count在 + 1前是 < 0 的,所以需要唤醒线程2
线程2现在可以正常访问了

dispatch_semaphore_wait的 timeout 参数和返回值

intptr_t
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)

dispatch_semaphore_wait timeout参数的作用:设置timeout (超时),在超时后自动放行,如果是超时引起的,返回值是非0;如果是dispatch_semaphore_signal唤醒,返回值是0;

总结
dispatch_semaphore_create():创建一个信号量,参数指定信号量数量
dispatch_semaphore_wait():使当前信号量的数量-1,如果数量<0,则阻塞当前线程,否则放行
dispatch_semaphore_signal():使当前信号量的数量+1,如果+之前数量是<0,唤醒被阻塞的线程。

你可能感兴趣的:(对dispatch_semaphore_t的理解)