美文网首页selector
『ios』dispatch_semaphore信号量的源码分析

『ios』dispatch_semaphore信号量的源码分析

作者: butterflyer | 来源:发表于2021-03-09 11:00 被阅读0次

信号量dispatch_semaphore_t
其实我们只需要用的create wait signal就可以了。
如果你想要一起学习,那么跟着来吧。看完一定会有所收获。

 // 创建信号量对象 信号量 >= 0
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    // -1操作
    dispatch_wait(sem, DISPATCH_TIME_FOREVER);
    // +1 操作
   dispatch_semaphore_signal(sem);

一个一个方法来分析吧。

首先是creat方法

dispatch_semaphore_t sem = dispatch_semaphore_create(1);

1.当value大于0的情况下才可以继续执行
2.申请一块dispatch_semaphore_s的内存
3.类似于生成了链表结构

dispatch_semaphore_t dispatch_semaphore_create(long value){
    dispatch_semaphore_t dsema;

    if (value < 0) {
        return NULL;
    }
    // 申请内存
    dsema = calloc(1, sizeof(struct dispatch_semaphore_s));
    
    if (fastpath(dsema)) {
        // do_vtable里面主要包含了这个 dispatch_semaphore_s 的操作函数
        dsema->do_vtable = &_dispatch_semaphore_vtable;

        //可以理解为链表的结尾标记
        dsema->do_next = DISPATCH_OBJECT_LISTLESS;

        // 引用计数
        dsema->do_ref_cnt = 1;
        dsema->do_xref_cnt = 1;

        // 目标队列
        dsema->do_targetq = dispatch_get_global_queue(0, 0);

        // 信号值
        dsema->dsema_value = value;
        dsema->dsema_orig = value;
    }
    
    return dsema;
}

_dispatch_semaphore_vtable内部结构

const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable = {
    .do_type = DISPATCH_SEMAPHORE_TYPE,
    .do_kind = "semaphore",
    .do_dispose = _dispatch_semaphore_dispose,
    .do_debug = _dispatch_semaphore_debug,
};

_dispatch_semaphore_dispose销毁信号量的意思

void _dispatch_semaphore_dispose(dispatch_semaphore_t dsema){
    kern_return_t kr;
    
    if (dsema->dsema_value < dsema->dsema_orig) {
        DISPATCH_CLIENT_CRASH("Semaphore/group object deallocated while in use");
    }
    
    if (dsema->dsema_port) {
        kr = semaphore_destroy(mach_task_self(), dsema->dsema_port);
        DISPATCH_SEMAPHORE_VERIFY_KR(kr);
    }
    if (dsema->dsema_waiter_port) {
        kr = semaphore_destroy(mach_task_self(), dsema->dsema_waiter_port);
        DISPATCH_SEMAPHORE_VERIFY_KR(kr);
    }
    
    _dispatch_dispose(dsema);
}

下面是dispatch_semaphore_wait函数

1.减一操作,如果剪完小于0的话,就阻塞信号量。

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout){
    
        // 如果信号量的值-1之后大于等于0,表示有资源可用
    if (dispatch_atomic_dec(&dsema->dsema_value) >= 0) {
        return 0;
    }
    return _dispatch_semaphore_wait_slow(dsema, timeout);
}

2.如果发生了阻塞。那么dispatch_time_t timeout就分为三种情况。
(1). DISPATCH_TIME_NOW 不等待立刻返回,KERN_OPERATION_TIMED_OUT超时,并将
dsema.value加1.
(2). DISPATCH_TIME_FOREVER无限等待, kr = semaphore_wait(dsema->dsema_port);
(3).计时等待,直到信号量到来,或超时

static long _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout){
    mach_timespec_t _timeout;
    kern_return_t kr;
    uint64_t nsec;
    long orig;
    
again:
    _dispatch_semaphore_create_port(&dsema->dsema_port);

    switch (timeout) {
    default:
        do {
            // timeout() already calculates relative time left
            nsec = _dispatch_timeout(timeout);
            _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
            _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
            kr = slowpath(semaphore_timedwait(dsema->dsema_port, _timeout));
        } while (kr == KERN_ABORTED);

        if (kr != KERN_OPERATION_TIMED_OUT) {
            DISPATCH_SEMAPHORE_VERIFY_KR(kr);
            break;
        }
    // Fall through and try to undo what the fast path did to dsema->dsema_value
    case DISPATCH_TIME_NOW:
        while ((orig = dsema->dsema_value) < 0) {
            if (dispatch_atomic_cmpxchg(&dsema->dsema_value, orig, orig + 1)) {
                return KERN_OPERATION_TIMED_OUT;
            }
        }
    // Another thread called semaphore_signal().
        // Fall through and drain the wakeup.
    case DISPATCH_TIME_FOREVER:
        do {
            kr = semaphore_wait(dsema->dsema_port);
        } while (kr == KERN_ABORTED);
        DISPATCH_SEMAPHORE_VERIFY_KR(kr);
        break;
    }

    goto again;
}

dispatch_semaphore_signal

发送信号量,如果value>0,则说明没有阻塞,直接返回0。

DISPATCH_NOINLINE long dispatch_semaphore_signal(dispatch_semaphore_t dsema){
    if (dispatch_atomic_inc(&dsema->dsema_value) > 0) {
        return 0;
    }
    return _dispatch_semaphore_signal_slow(dsema);
}

如果小于0,_dispatch_semaphore_signal_slow函数, kr = semaphore_signal(dsema->dsema_port);发送信号量,来唤醒semaphore_wait(dsema->dsema_port)。

DISPATCH_NOINLINE static long _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema){
    kern_return_t kr;
    
    _dispatch_semaphore_create_port(&dsema->dsema_port);

    // Before dsema_sent_ksignals is incremented we can rely on the reference
    // held by the waiter. However, once this value is incremented the waiter
    // may return between the atomic increment and the semaphore_signal(),
    // therefore an explicit reference must be held in order to safely access
    // dsema after the atomic increment.
    _dispatch_retain(dsema);
    
    dispatch_atomic_inc(&dsema->dsema_sent_ksignals);
    
    // 利用系统的信号量库实现发送信号量的功能
    kr = semaphore_signal(dsema->dsema_port);
    DISPATCH_SEMAPHORE_VERIFY_KR(kr);

    _dispatch_release(dsema);
    
    return 1;
}

自己分析一遍绝对可以增强响应的理解,可以让自己的代码更加健壮。

相关文章

网友评论

    本文标题:『ios』dispatch_semaphore信号量的源码分析

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