GCD之dispatch_semaphore源碼剖析

一、簡單介紹

上一篇介紹了dispatch_once的實現(xiàn)原理,這一篇將會對dispatch_semaphore進行源碼探究肥荔,關(guān)于信號量的API并不多,主要是三個:create朝群、wait燕耿、signal。

Apple對信號量的描述:
您可以使用調(diào)度信號來調(diào)節(jié)允許同時訪問有限資源的任務(wù)數(shù)量姜胖。
例如誉帅,每個應(yīng)用程序都有一定數(shù)量的文件描述符可供使用。
如果您有一個處理大量文件的任務(wù)右莱,那么您不想同時打開太多的文件而耗盡文件描述符堵第,那么您可以使用信號量來限制文件處理代碼最多使用的文件描述符的數(shù)量。

聽起來比較費勁隧出,舉個 百度百科上停車位的例子就一目了然了:

以一個停車場的運作為例踏志。簡單起見,假設(shè)停車場只有三個車位胀瞪,一開始三個車位都是空的针余。這時如果同時來了五輛車,看門人允許其中三輛直接進入凄诞,然后放下車攔圆雁,剩下的車則必須在入口等待,此后來的車也都不得不在入口處等待帆谍。這時伪朽,有一輛車離開停車場,看門人得知后汛蝙,打開車攔烈涮,放入外面的一輛進去朴肺,如果又離開兩輛,則又可以放入兩輛坚洽,如此往復(fù)戈稿。在這個停車場系統(tǒng)中,車位是公共資源讶舰,每輛車好比一個線程鞍盗,看門人起的就是信號量的作用

二、dispatch semaphore工作原理

dispatch_semaphore與傳統(tǒng)信號量工作原理類似跳昼。但是在資源可用的情況下般甲,使用GCD semaphore將會消耗較少的時間,因為在這種情況下GCD不會調(diào)用內(nèi)核鹅颊,只有在資源不可用的時候才會調(diào)用內(nèi)核敷存,并且系統(tǒng)需要停在你的線程里,直到線程發(fā)出可用信號挪略。

三历帚、源碼

semaphore.h

/*!
 * @function dispatch_semaphore_create
 *
 * @abstract
 * 用初始值創(chuàng)建新的計數(shù)信號量。
 *
 * @discussion
 * 當(dāng)兩個線程需要協(xié)調(diào)特定事件的完成時杠娱,將值傳遞給零值非常有用挽牢。
 * 傳遞大于零的值對于管理池大小等于該值的有限資源池非常有用
 *
 * @param value
 * 信號量的初始值.傳遞一個小于零的值將導(dǎo)致返回NULL
 *
 * @result
 * 創(chuàng)建成功返回創(chuàng)建的信號量,創(chuàng)建失敗返回NULL.
 */
  dispatch_semaphore_t 
  dispatch_semaphore_create(long value);

/*!
 * @function dispatch_semaphore_wait
 *
 * @abstract
 * 等待(遞減)信號量摊求。
 *
 * @discussion
 * 信號量減一禽拔,如果結(jié)果值小于零,則此函數(shù)在返回之前以FIFO順序等待室叉。
 *
 * @param dsema
 * 信號量
 *
 * @param timeout
 * 何時超時(請參閱dispatch_time)睹栖。 為了方便起見,有DISPATCH_TIME_NOW = 0和DISPATCH_TIME_FOREVER = ~0ull兩個常量茧痕。
 *
 * @result
 * 成功時返回零野来,如果發(fā)生超時則返回非零值
 */
  long 
  dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

/*!
 * @function dispatch_semaphore_signal
 *
 * @abstract
 * 信號(增加)一個信號量
 *
 * @discussion
 * 信號量加1。如果前一個值小于零踪旷,則此函數(shù)在返回之前喚醒等待的線程
 *
 * @param dsema
 * 在這個參數(shù)中傳遞NULL的結(jié)果是未定義的.
 *
 * @result
 * 如果線程被喚醒曼氛,該函數(shù)返回非零值。否則令野,返回零.
 */
  long
  dispatch_semaphore_signal(dispatch_semaphore_t dsema);

以上就是關(guān)于信號量的聲明文件舀患,沒有什么好講的,我也對源碼進行了詳盡的注釋气破,如果還有問題請留言聊浅。

semaphore.c關(guān)于信號量部分

#pragma mark -
#pragma mark dispatch_semaphore_t

struct dispatch_semaphore_vtable_s {
    DISPATCH_VTABLE_HEADER(dispatch_semaphore_s);
};

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_t dispatch_semaphore_create(long value)
{
    dispatch_semaphore_t dsema;

    // 如果內(nèi)部值為負(fù)數(shù),則該值的絕對值等于等待線程的數(shù)量。因此低匙,用負(fù)值初始化信號量是虛假的
    if (value < 0) {
        return NULL;
    }

    dsema = calloc(1, sizeof(struct dispatch_semaphore_s));

    /*
     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_QUEUE_PRIORITY_DEFAULT優(yōu)先級的全局隊列:
     {
         .do_vtable = &_dispatch_queue_root_vtable,
         .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
         .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
         .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK,
         .do_vtable = &_dispatch_semaphore_vtable,
         .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
         .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
         .dgq_thread_pool_size = MAX_THREAD_COUNT,
         .dq_label = "com.apple.root.default-overcommit-priority",
         .dq_running = 2,
         .dq_width = UINT32_MAX,
         .dq_serialnum = 7,
     }
     */
    if (fastpath(dsema)) {
        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(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dsema->dsema_value = value;
        dsema->dsema_orig = value;
#if USE_POSIX_SEM
        int ret = sem_init(&dsema->dsema_sem, 0, 0);
        DISPATCH_SEMAPHORE_VERIFY_RET(ret);
#endif
    }

    return dsema;
}

#if USE_MACH_SEM
static void _dispatch_semaphore_create_port(semaphore_t *s4) {
    kern_return_t kr;
    semaphore_t tmp;

    if (*s4) {
        return;
    }

    // lazily allocate the semaphore port

    // Someday:
    // 1) Switch to a doubly-linked FIFO in user-space.
    // 2) User-space timers for the timeout.
    // 3) Use the per-thread semaphore port.

    while ((kr = semaphore_create(mach_task_self(), &tmp, SYNC_POLICY_FIFO, 0))) {
        DISPATCH_VERIFY_MIG(kr);
        sleep(1);
    }

    if (!dispatch_atomic_cmpxchg(s4, 0, tmp)) {
        kr = semaphore_destroy(mach_task_self(), tmp);
        DISPATCH_SEMAPHORE_VERIFY_KR(kr);
    }

    _dispatch_safe_fork = false;
}
#endif

static void _dispatch_semaphore_dispose(dispatch_semaphore_t dsema)
{
    // 當(dāng)前可用資源數(shù)目小于原始值旷痕,表示有線程正在執(zhí)行任務(wù),不可被dispose
    if (dsema->dsema_value < dsema->dsema_orig) {
        printf("BUG IN CLIENT OF LIBDISPATCH: Semaphore/group object deallocated while in use");
    }

#if USE_MACH_SEM
    kern_return_t kr;
    // 銷毀dsema_port
    if (dsema->dsema_port) {
        kr = semaphore_destroy(mach_task_self(), dsema->dsema_port);
        DISPATCH_SEMAPHORE_VERIFY_KR(kr);
    }
    // 有線程正在等待努咐,銷毀dsema_waiter_port
    if (dsema->dsema_waiter_port) {
        kr = semaphore_destroy(mach_task_self(), dsema->dsema_waiter_port);
        DISPATCH_SEMAPHORE_VERIFY_KR(kr);
    }
#elif USE_POSIX_SEM
    int ret = sem_destroy(&dsema->dsema_sem);
    DISPATCH_SEMAPHORE_VERIFY_RET(ret);
#endif

    _dispatch_dispose(dsema);
}

static size_t _dispatch_semaphore_debug(dispatch_semaphore_t dsema, char *buf, size_t bufsiz)
{
    size_t offset = 0;
    offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ",
            dx_kind(dsema), dsema);
    offset += _dispatch_object_debug_attr(dsema, &buf[offset], bufsiz - offset);
#if USE_MACH_SEM
    offset += snprintf(&buf[offset], bufsiz - offset, "port = 0x%u, ",
            dsema->dsema_port);
#endif
    offset += snprintf(&buf[offset], bufsiz - offset,
            "value = %ld, orig = %ld }", dsema->dsema_value, dsema->dsema_orig);
    return offset;
}

DISPATCH_NOINLINE
long
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
    _dispatch_retain(dsema);
    // 僅僅是將dsema->dsema_sent_ksignals值加1
    (void)dispatch_atomic_inc2o(dsema, dsema_sent_ksignals);
    // 創(chuàng)建semaphore_t
    _dispatch_semaphore_create_port(&dsema->dsema_port);
    // 核心:利用系統(tǒng)的信號量庫實現(xiàn)發(fā)送信號量的功能苦蒿,表示現(xiàn)在可用的資源數(shù)目+1殴胧,這里是可創(chuàng)建的用于并行線程數(shù)目+1
    kern_return_t kr = semaphore_signal(dsema->dsema_port);
    // 如果kr返回不為真渗稍,打印錯誤
    do {
        if (kr) {
            printf("BUG IN CLIENT OF LIBDISPATCH: flawed group/semaphore logic");
        }
    } while (0);
    _dispatch_release(dsema);
    return 1;
}

long dispatch_semaphore_signal(dispatch_semaphore_t dsema) {
    // dispatch_atomic_release_barrier();
    // __sync_add_and_fetch((p), (v))
    // dispatch_atomic_inc2o(dsema, dsema_value)
    long value = dsema->dsema_value + 1;
    if (value > 0) {
        return 0;
    }
    
    if (slowpath(value == LONG_MIN)) {// 輸出錯誤日志
        printf("BUG IN CLIENT OF LIBDISPATCH: Unbalanced call to dispatch_semaphore_signal()");
    }
    
    return _dispatch_semaphore_signal_slow(dsema);
}

DISPATCH_NOINLINE
static long
_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
    long orig;
again:
    // Mach信號似乎有時會虛假地醒來,因此,我們保持一個Mach信號量被發(fā)信號的次數(shù)的平行計數(shù)6880961
    // 判斷dsema->dsema_sent_ksignals與orig是否相等团滥,如果相等就返回YES竿屹,并將orig - 1的值賦給dsema->dsema_sent_ksignals
    while ((orig = dsema->dsema_sent_ksignals)) {
        if ((long)(dsema->dsema_sent_ksignals) == orig) {
            dsema->dsema_sent_ksignals = orig - 1;
            return 0;
        }
    }
#if USE_MACH_SEM
    mach_timespec_t _timeout;
    kern_return_t kr;

    _dispatch_semaphore_create_port(&dsema->dsema_port);

    // From xnu/osfmk/kern/sync_sema.c:
    // wait_semaphore->count = -1; /* we don't keep an actual count */
    //
    // The code above does not match the documentation, and that fact is
    // not surprising. The documented semantics are clumsy to use in any
    // practical way. The above hack effectively tricks the rest of the
    // Mach semaphore logic to behave like the libdispatch algorithm.

    switch (timeout) {
        default:
            do {
                uint64_t 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_cmpxchg2o(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;
    }
#elif USE_POSIX_SEM
    struct timespec _timeout;
    int ret;

    switch (timeout) {
        default:
            do {
                uint64_t 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);
                ret = slowpath(sem_timedwait(&dsema->dsema_sem, &_timeout));
            } while (ret == -1 && errno == EINTR);

            if (ret == -1 && errno != ETIMEDOUT) {
                DISPATCH_SEMAPHORE_VERIFY_RET(ret);
                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_cmpxchg2o(dsema, dsema_value, orig, orig + 1)) {
                    errno = ETIMEDOUT;
                    return -1;
                }
            }
            // Another thread called semaphore_signal().
            // Fall through and drain the wakeup.
        case DISPATCH_TIME_FOREVER:
            do {
                ret = sem_wait(&dsema->dsema_sem);
            } while (ret != 0);
            DISPATCH_SEMAPHORE_VERIFY_RET(ret);
            break;
    }
#endif
    goto again;
}

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
    // 調(diào)用GCC內(nèi)置的函數(shù)__sync_sub_and_fetch,實現(xiàn)減法的原子性操作灸姊。
    // __sync_sub_and_fetch(p, v)
    // long value = dsema->dsema_value - 1;
    long value = dispatch_atomic_dec2o(dsema, dsema_value);
    
    // value大于等于0 就立刻返回
    if (fastpath(value >= 0)) {
        return 0;
    }
    // value < 0 進入等待狀態(tài)
    return _dispatch_semaphore_wait_slow(dsema, timeout);
}

四拱燃、源碼剖析

先講頭文件聲明的那三個函數(shù)即信號量的create、wait力惯、signal

1.信號量的創(chuàng)建

dispatch_semaphore_t dispatch_semaphore_create(long value)
{
    dispatch_semaphore_t dsema;
    #1
    if (value < 0) {
        return NULL;
    }
    #2
    dsema = calloc(1, sizeof(struct dispatch_semaphore_s));
    #3
    if (dsema) {
        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(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dsema->dsema_value = value;
        dsema->dsema_orig = value;
    }
    return dsema;
}

注:只研究USE_MACH_SEM情況碗誉,其他操作系統(tǒng)請自行查看
這個函數(shù)就一個參數(shù),這個值的含義就是允許最大并行執(zhí)行線程的數(shù)目父晶。
1哮缺、判斷value參數(shù)的合法性
2、分配內(nèi)存
3甲喝、填充信號量結(jié)構(gòu)體尝苇,返回信號量

do_vtable = &_dispatch_queue_root_vtable
dsema->do_targetq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
展開之后:
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_QUEUE_PRIORITY_DEFAULT優(yōu)先級的全局隊列:
{
    .do_vtable = &_dispatch_queue_root_vtable,
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
    .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK,
    .do_vtable = &_dispatch_semaphore_vtable,
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
    .dgq_thread_pool_size = MAX_THREAD_COUNT,
    .dq_label = "com.apple.root.default-overcommit-priority",
    .dq_running = 2,
    .dq_width = UINT32_MAX,
    .dq_serialnum = 7,
}

2、信號量的等待

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
    // 調(diào)用GCC內(nèi)置的函數(shù)__sync_sub_and_fetch埠胖,實現(xiàn)減法的原子性操作糠溜。
    // __sync_sub_and_fetch(p, v)
    // long value = dsema->dsema_value - 1;
    long value = dispatch_atomic_dec2o(dsema, dsema_value);
    
    // value大于等于0 就立刻返回
    if (fastpath(value >= 0)) {
        return 0;
    }
    // value < 0 進入等待狀態(tài)
    return _dispatch_semaphore_wait_slow(dsema, timeout);
}

注:真正實現(xiàn)等待邏輯的函數(shù)是最后一行代碼_dispatch_semaphore_wait_slow()函數(shù)

static long
_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
    long orig;
again:
    // Mach信號似乎有時會虛假地醒來,因此,我們保持一個Mach信號量被發(fā)信號的次數(shù)的平行計數(shù)6880961
    // 判斷dsema->dsema_sent_ksignals與orig是否相等直撤,如果相等就返回YES非竿,并將orig - 1的值賦給dsema->dsema_sent_ksignals
    while ((orig = dsema->dsema_sent_ksignals)) {
        if ((long)(dsema->dsema_sent_ksignals) == orig) {
            dsema->dsema_sent_ksignals = orig - 1;
            return 0;
        }
    }
    mach_timespec_t _timeout;
    kern_return_t kr;

    _dispatch_semaphore_create_port(&dsema->dsema_port);

    // From xnu/osfmk/kern/sync_sema.c:
    // wait_semaphore->count = -1; /* we don't keep an actual count */
    //
    // The code above does not match the documentation, and that fact is
    // not surprising. The documented semantics are clumsy to use in any
    // practical way. The above hack effectively tricks the rest of the
    // Mach semaphore logic to behave like the libdispatch algorithm.

    switch (timeout) {
        default:
            do {
                uint64_t 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_cmpxchg2o(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;
}

刪掉多余操作系統(tǒng)的分支,剩下的代碼很簡練谋竖,這個函數(shù)針對不同的 timeout 參數(shù)红柱,分了三種情況考慮:
①DISPATCH_TIME_NOW它的值為0,也就是說超時時間為0圈盔,如果是這種情況的話豹芯,相當(dāng)于

while ((orig = dsema->dsema_value) < 0) {
    if (dsema->dsema_value == orig) {
         dsema->dsema_value = orig + 1;
         return KERN_OPERATION_TIMED_OUT;
    }       
}     

函數(shù)立即返回值KERN_OPERATION_TIMED_OUT即49
②DISPATCH_TIME_FOREVER它的值為0按位取反,是一個很小的負(fù)數(shù)
這時執(zhí)行系統(tǒng)庫的等待函數(shù)semaphore_wait驱敲,直到semaphore_wait返回KERN_ABORTED即為14為止铁蹈。
③default分支,我們指定一個超時時間,這和 DISPATCH_TIME_FOREVER 的處理比較類似
不同的是我們調(diào)用了內(nèi)核提供的semaphore_timedwait方法可以指定超時時間

可見信號量被喚醒后握牧,會回到最開始的地方容诬,進入while循環(huán)。
這個判斷條件一般都會成立
進入while循環(huán)后沿腰,if 判斷一定成立览徒,因此返回0
正如文檔所說,返回 0 表示成功颂龙,否則表示超時

3习蓬、信號量的signal(實在不知道怎么翻譯啊~~)

long dispatch_semaphore_signal(dispatch_semaphore_t dsema) {
    // dispatch_atomic_release_barrier();
    // __sync_add_and_fetch((p), (v))
    // dispatch_atomic_inc2o(dsema, dsema_value)
    long value = dsema->dsema_value + 1;
    if (value > 0) {
        return 0;
    }
    
    if (slowpath(value == LONG_MIN)) {// 輸出錯誤日志
        printf("BUG IN CLIENT OF LIBDISPATCH: Unbalanced call to dispatch_semaphore_signal()");
    }
    
    return _dispatch_semaphore_signal_slow(dsema);
}

這個函數(shù)真正的實現(xiàn)是在_dispatch_semaphore_signal_slow()函數(shù)里面。

long
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
    _dispatch_retain(dsema);
    // 僅僅是將dsema->dsema_sent_ksignals值加1
    (void)dispatch_atomic_inc2o(dsema, dsema_sent_ksignals);
    // 創(chuàng)建semaphore_t
    _dispatch_semaphore_create_port(&dsema->dsema_port);
    // 核心:利用系統(tǒng)的信號量庫實現(xiàn)發(fā)送信號量的功能措嵌,表示現(xiàn)在可用的資源數(shù)目+1躲叼,這里是可創(chuàng)建的用于并行線程數(shù)目+1
    kern_return_t kr = semaphore_signal(dsema->dsema_port);
    // 如果kr返回不為真,打印錯誤
    do {
        if (kr) {
            printf("BUG IN CLIENT OF LIBDISPATCH: flawed group/semaphore logic");
        }
    } while (0);
    _dispatch_release(dsema);
    return 1;
}

注:核心代碼就是semaphore_signal(),利用系統(tǒng)的信號量庫實現(xiàn)發(fā)送信號量的功能企巢,表示現(xiàn)在可用的資源數(shù)目+1枫慷,這里是可創(chuàng)建的用于并行執(zhí)行的線程數(shù)目+1,如果發(fā)現(xiàn)這與源碼有出入的話浪规,那就是我對一些宏進行了展開或听,還有一些無用的代碼進行了刪減,并不影響我們理解原理笋婿。

至此誉裆,我們把信號量的源碼剖析完了,下一篇繼續(xù)分析semaphore.c實現(xiàn)文件里面其余實現(xiàn)萌抵,主要分析的是dispatch_group相關(guān)的實現(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末找御,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绍填,更是在濱河造成了極大的恐慌霎桅,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讨永,死亡現(xiàn)場離奇詭異滔驶,居然都是意外死亡,警方通過查閱死者的電腦和手機卿闹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門揭糕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锻霎,你說我怎么就攤上這事著角。” “怎么了旋恼?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵吏口,是天一觀的道長。 經(jīng)常有香客問我,道長产徊,這世上最難降的妖魔是什么昂勒? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮舟铜,結(jié)果婚禮上戈盈,老公的妹妹穿的比我還像新娘。我一直安慰自己谆刨,他們只是感情好塘娶,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痴荐,像睡著了一般血柳。 火紅的嫁衣襯著肌膚如雪官册。 梳的紋絲不亂的頭發(fā)上生兆,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音膝宁,去河邊找鬼鸦难。 笑死,一個胖子當(dāng)著我的面吹牛员淫,可吹牛的內(nèi)容都是我干的合蔽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼介返,長吁一口氣:“原來是場噩夢啊……” “哼拴事!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起圣蝎,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤刃宵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后徘公,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牲证,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年关面,在試婚紗的時候發(fā)現(xiàn)自己被綠了坦袍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡等太,死狀恐怖捂齐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缩抡,我是刑警寧澤奠宜,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響挎塌,放射性物質(zhì)發(fā)生泄漏徘六。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一榴都、第九天 我趴在偏房一處隱蔽的房頂上張望待锈。 院中可真熱鬧,春花似錦嘴高、人聲如沸竿音。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽春瞬。三九已至,卻和暖如春套啤,著一層夾襖步出監(jiān)牢的瞬間宽气,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工潜沦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萄涯,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓唆鸡,卻偏偏與公主長得像涝影,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子争占,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容