概述
GCD
是一套強大的多線程方案拗慨,提供了多種任務(wù)隊列來提高開發(fā)效率,通過閱讀libdispatch
的源碼可以更好的理解GCD
的工作流程韩脏,幫助我們設(shè)計更好的代碼
結(jié)構(gòu)類型
libdispatch
使用宏定義實現(xiàn)了大量的模板結(jié)構(gòu)類型脉让,除此之外還使用了union
和enum
結(jié)合的方式實現(xiàn)動態(tài)參數(shù)類型的靈活性:
-
queue_type
:隊列類型晌杰,例如全局隊列 -
source_type
:資源統(tǒng)稱跷睦,queue
或者function
都可以看做是一個資源 -
semaphore_type
:信號類型,信號可以保證資源同時多線程競爭下的安全 -
continuation_type
:派發(fā)任務(wù)會被封裝成dispatch_continuation_t
肋演,然后被壓入隊列中
enum {
_DISPATCH_CONTINUATION_TYPE = 0x00000, // meta-type for continuations
_DISPATCH_QUEUE_TYPE = 0x10000, // meta-type for queues
_DISPATCH_SOURCE_TYPE = 0x20000, // meta-type for sources
_DISPATCH_SEMAPHORE_TYPE = 0x30000, // meta-type for semaphores
_DISPATCH_ATTR_TYPE = 0x10000000, // meta-type for attribute structures
DISPATCH_CONTINUATION_TYPE = _DISPATCH_CONTINUATION_TYPE,
DISPATCH_QUEUE_ATTR_TYPE = _DISPATCH_QUEUE_TYPE | _DISPATCH_ATTR_TYPE,
DISPATCH_QUEUE_TYPE = 1 | _DISPATCH_QUEUE_TYPE,
DISPATCH_QUEUE_GLOBAL_TYPE = 2 | _DISPATCH_QUEUE_TYPE,
DISPATCH_QUEUE_MGR_TYPE = 3 | _DISPATCH_QUEUE_TYPE,
DISPATCH_SEMAPHORE_TYPE = _DISPATCH_SEMAPHORE_TYPE,
DISPATCH_SOURCE_ATTR_TYPE = _DISPATCH_SOURCE_TYPE | _DISPATCH_ATTR_TYPE,
DISPATCH_SOURCE_KEVENT_TYPE = 1 | _DISPATCH_SOURCE_TYPE,
};
對于libdispatch
的結(jié)構(gòu)體類型來說抑诸,都存在DISPATCH_STRUCT_HEADER(x)
類型的變量。通過##
拼接變量名的方式對不同的類型生成動態(tài)的參數(shù)變量
#define DISPATCH_STRUCT_HEADER(x) \
_OS_OBJECT_HEADER( \
const struct dispatch_##x##_vtable_s *do_vtable, \
do_ref_cnt, \
do_xref_cnt); \
struct dispatch_##x##_s *volatile do_next; \
struct dispatch_queue_s *do_targetq; \
void *do_ctxt; \
void *do_finalizer; \
unsigned int do_suspend_cnt;
聯(lián)合體union
保證了各變量享用同一個內(nèi)存地址爹殊,這也意味著各變量是互斥的哼鬓。通過聯(lián)合體結(jié)構(gòu),libdispatch
實現(xiàn)了類型強制轉(zhuǎn)換的效果边灭。另外异希,通過宏定義DISPATCH_DECL(name)
來保證所有dispatch_xxx_t
變量實際上是一個指向dispatch_xxx_s
的指針:
typedef union {
struct _os_object_s *_os_obj;
struct dispatch_object_s *_do;
struct dispatch_continuation_s *_dc;
struct dispatch_queue_s *_dq;
struct dispatch_queue_attr_s *_dqa;
struct dispatch_group_s *_dg;
struct dispatch_source_s *_ds;
struct dispatch_source_attr_s *_dsa;
struct dispatch_semaphore_s *_dsema;
struct dispatch_data_s *_ddata;
struct dispatch_io_s *_dchannel;
struct dispatch_operation_s *_doperation;
struct dispatch_disk_s *_ddisk;
} dispatch_object_t __attribute__((__transparent_union__));
#define DISPATCH_DECL(name) typedef struct name##_s *name##_t
一方面,union
在使用時會分配一塊足夠大的內(nèi)存(能夠容納任一一種類型)绒瘦,這意味著可以隨時更換存儲數(shù)據(jù)称簿,同樣可能造成數(shù)據(jù)的破壞;另一方面惰帽,它讓C
函數(shù)擁有了返回參數(shù)多樣化的靈活性憨降。但是要記住不同的數(shù)據(jù)類型在生成自身的do_vtable
也有不同的表現(xiàn):
/// GCD數(shù)據(jù)類型vtable屬性初始化
DISPATCH_VTABLE_INSTANCE(semaphore,
.do_type = DISPATCH_SEMAPHORE_TYPE,
.do_kind = "semaphore",
.do_dispose = _dispatch_semaphore_dispose,
.do_debug = _dispatch_semaphore_debug,
);
DISPATCH_VTABLE_INSTANCE(group,
.do_type = DISPATCH_GROUP_TYPE,
.do_kind = "group",
.do_dispose = _dispatch_semaphore_dispose,
.do_debug = _dispatch_semaphore_debug,
);
DISPATCH_VTABLE_INSTANCE(queue,
.do_type = DISPATCH_QUEUE_TYPE,
.do_kind = "queue",
.do_dispose = _dispatch_queue_dispose,
.do_invoke = NULL,
.do_probe = (void *)dummy_function_r0,
.do_debug = dispatch_queue_debug,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_root, queue,
.do_type = DISPATCH_QUEUE_GLOBAL_TYPE,
.do_kind = "global-queue",
.do_debug = dispatch_queue_debug,
.do_probe = _dispatch_queue_probe_root,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_mgr, queue,
.do_type = DISPATCH_QUEUE_MGR_TYPE,
.do_kind = "mgr-queue",
.do_invoke = _dispatch_mgr_thread,
.do_debug = dispatch_queue_debug,
.do_probe = _dispatch_mgr_wakeup,
);
DISPATCH_VTABLE_INSTANCE(queue_specific_queue,
.do_type = DISPATCH_QUEUE_SPECIFIC_TYPE,
.do_kind = "queue-context",
.do_dispose = _dispatch_queue_specific_queue_dispose,
.do_invoke = NULL,
.do_probe = (void *)dummy_function_r0,
.do_debug = (void *)dispatch_queue_debug,
);
DISPATCH_VTABLE_INSTANCE(queue_attr,
.do_type = DISPATCH_QUEUE_ATTR_TYPE,
.do_kind = "queue-attr",
);
DISPATCH_VTABLE_INSTANCE(source,
.do_type = DISPATCH_SOURCE_KEVENT_TYPE,
.do_kind = "kevent-source",
.do_invoke = _dispatch_source_invoke,
.do_dispose = _dispatch_source_dispose,
.do_probe = _dispatch_source_probe,
.do_debug = _dispatch_source_debug,
);
以queue
為例,其結(jié)構(gòu)類型為dispatch_queue_s
该酗,鑒于源碼中使用了大量的宏定義增加屬性授药,下面的結(jié)構(gòu)是替換一部分宏定義后的結(jié)構(gòu):
struct dispatch_queue_s {
DISPATCH_STRUCT_HEADER(queue);
uint32_t volatile dq_running;
uint32_t dq_width;
struct dispatch_object_s *volatile dq_items_tail;
struct dispatch_object_s *volatile dq_items_head;
unsigned long dq_serialnum;
dispatch_queue_t dq_specific_q;
char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE];
char _dq_pad[DISPATCH_QUEUE_CACHELINE_PAD];
};
一個線程任務(wù)隊列有這么幾個重要的屬性:
do_vtable
虛擬表,采用類似于C++
的模板方式定義了一個結(jié)構(gòu)體vtable
呜魄,主要存儲了結(jié)構(gòu)類型相關(guān)的描述信息do_targetq
目標(biāo)隊列悔叽,GCD
允許我們將某個任務(wù)隊列指派到另外的任務(wù)隊列中執(zhí)行。當(dāng)do_targetq
不為空時爵嗅,async
的實現(xiàn)有會所不同dq_width
最大并發(fā)數(shù)娇澎,串行/主線程的這個值是1
-
do_ctxt
線程上下文,用來存儲線程池相關(guān)數(shù)據(jù)睹晒,比如用于線程掛起和喚醒的信號量趟庄、線程池尺寸等struct dispatch_root_queue_context_s { union { struct { unsigned int volatile dgq_pending; dispatch_semaphore_t dgq_thread_mediator; uint32_t dgq_thread_pool_size; }; char _dgq_pad[DISPATCH_CACHELINE_SIZE]; }; };
do_suspend_cnt
用作暫停標(biāo)記,當(dāng)大于等于2時表示任務(wù)為延時任務(wù)伪很。在任務(wù)達(dá)到時會修改標(biāo)記戚啥,然后喚醒隊列調(diào)度任務(wù)
派發(fā)任務(wù)會被包裝成dispatch_continuation_s
結(jié)構(gòu)體對象,同樣
#define DISPATCH_OBJ_ASYNC_BIT 0x1
#define DISPATCH_OBJ_BARRIER_BIT 0x2
#define DISPATCH_OBJ_GROUP_BIT 0x4
#define DISPATCH_OBJ_SYNC_SLOW_BIT 0x8
#define DISPATCH_CONTINUATION_HEADER(x) \
_OS_OBJECT_HEADER( \
const void *do_vtable, \
do_ref_cnt, \
do_xref_cnt); \
struct dispatch_##x##_s *volatile do_next; \
dispatch_function_t dc_func; \
void *dc_ctxt; \
void *dc_data; \
void *dc_other;
struct dispatch_continuation_s {
DISPATCH_CONTINUATION_HEADER(continuation);
};
一個dispatch_continuation_s
變量不總是只包裝了單個任務(wù)锉试,它被設(shè)置成復(fù)用機制猫十,通過TSD
的方式保證每個線程可以擁有一定數(shù)量的復(fù)用continuation
,以此來減少不必要的內(nèi)存分配開銷。
dc_func
承擔(dān)執(zhí)行任務(wù)的對象炫彩,宏定義_dispatch_client_callout
最終會以dc_func(dc_ctxt)
的方式回調(diào)dc_ctxt
存儲了continuation
對象的上下文數(shù)據(jù),同樣用于執(zhí)行任務(wù)do_vtable
只有當(dāng)do_vtable
的值小于127
時才表示變量是一個continuation
絮短,派發(fā)到主/串行隊列的任務(wù)會被標(biāo)記DISPATCH_OBJ_BARRIER_BIT
屏障標(biāo)記
libdispatch
的結(jié)構(gòu)對象都擁有自己的一張do_vtable
虛擬表江兢,同樣采用模板式的方式生成,每一個具體的結(jié)構(gòu)類型會生成一張對應(yīng)類型的虛擬表丁频,但是屬性基本是統(tǒng)一的
#define DISPATCH_VTABLE_HEADER(x) \
unsigned long const do_type; \
const char *const do_kind; \
size_t (*const do_debug)(struct dispatch_##x##_s *, char *, size_t); \
struct dispatch_queue_s *(*const do_invoke)(struct dispatch_##x##_s *); \
bool (*const do_probe)(struct dispatch_##x##_s *); \
void (*const do_dispose)(struct dispatch_##x##_s *)
虛擬表采用類型名拼接
的方式生成不同類型的重載函數(shù)杉允,由于libdispatch
類型采用union
結(jié)構(gòu),這兩者結(jié)合極大的保證了執(zhí)行的靈活性
do_type
數(shù)據(jù)的具體類型席里,詳見上文中的枚舉值do_kind
數(shù)據(jù)的類型描述字符串叔磷,比如全局隊列為global-queue
do_debug
debug
方法,用來獲取調(diào)試時需要的變量信息字符串do_probe
用于檢測傳入對象中的一些值是否滿足條件do_invoke
喚醒隊列的方法奖磁,全局隊列和主隊列此項為NULL
disaptch_root_queue_context_s
存儲了線程運行過程中的上下文數(shù)據(jù)改基,默認(rèn)情況下已經(jīng)創(chuàng)建了多個全局的上下文對象
struct dispatch_root_queue_context_s {
union {
struct {
unsigned int volatile dgq_pending;
#if DISPATCH_USE_PTHREAD_POOL
dispatch_semaphore_t dgq_thread_mediator;
uint32_t dgq_thread_pool_size;
#endif
};
char _dgq_pad[DISPATCH_CACHELINE_SIZE];
};
};
上下文對象在執(zhí)行任務(wù)的過程中存儲了線程信號信息以及可用線程池數(shù)量。
dgq_pending
咖为、_dgq_pad
當(dāng)前版本源碼中未使用dgq_thread_mediator
用于判斷是否存在可用線程資源秕狰,如果存在返回1
,否則后續(xù)將創(chuàng)建新線程執(zhí)行任務(wù)dgq_thread_pool_size
線程池剩余可用數(shù)量躁染,只有dgq_thread_mediator
的查詢返回0
并且此項大于0
時會嘗試創(chuàng)建新線程用以執(zhí)行任務(wù)
GCD
創(chuàng)建了總共八個四種優(yōu)先級的全局上下文對象,每個上下文最多可容納255
個線程數(shù)量
#define MAX_THREAD_COUNT 255
DISPATCH_CACHELINE_ALIGN
static struct dispatch_root_queue_context_s _dispatch_root_queue_contexts[] = {
[DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
[DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
[DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
[DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
[DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
[DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY] = {{{
#if DISPATCH_USE_PTHREAD_POOL
.dgq_thread_mediator = &_dispatch_thread_mediator[
DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY],
.dgq_thread_pool_size = MAX_THREAD_COUNT,
#endif
}}},
};
dispatch_semaphore_s
是性能稍次于自旋鎖的的信號量對象,用來保證資源使用的安全性臭杰。
struct dispatch_semaphore_s {
DISPATCH_STRUCT_HEADER(semaphore);
long dsema_value;
long dsema_orig;
size_t dsema_sent_ksignals;
size_t dsema_group_waiters;
struct dispatch_sema_notify_s *dsema_notify_head;
struct dispatch_sema_notify_s *dsema_notify_tail;
};
相比其他的結(jié)構(gòu)曹动,信號量的內(nèi)部要簡潔的多,主要使用的就三個屬性:
dsema_value
當(dāng)前信號值饰恕,當(dāng)這個值小于0時無法訪問加鎖資源dsema_orig
初始化信號值挠羔,限制了同時訪問資源的線程數(shù)量dsema_sent_ksignals
由于mach
信號可能會被意外喚醒,通過原子操作來避免虛假信號
通過一張圖表示上面提及的四種數(shù)據(jù)類型的關(guān)系:
線程私有變量
進(jìn)程中的全局變量和靜態(tài)變量是所有線程都能訪問的共享變量埋嵌,這意味著訪問這樣的數(shù)據(jù)需要昂貴的同步花銷褥赊,Thread-specific-Data
線程私有數(shù)據(jù)機制讓每一個線程擁有私有的全局變量。libdispatch
提供了四個pthread_key
來存取這些數(shù)據(jù)莉恼,在初始化階段進(jìn)行初始化:
DISPATCH_EXPORT DISPATCH_NOTHROW
void
libdispatch_init(void)
{
dispatch_assert(DISPATCH_QUEUE_PRIORITY_COUNT == 4);
dispatch_assert(DISPATCH_ROOT_QUEUE_COUNT == 8);
dispatch_assert(DISPATCH_QUEUE_PRIORITY_LOW ==
-DISPATCH_QUEUE_PRIORITY_HIGH);
dispatch_assert(countof(_dispatch_root_queues) ==
DISPATCH_ROOT_QUEUE_COUNT);
dispatch_assert(countof(_dispatch_root_queue_contexts) ==
DISPATCH_ROOT_QUEUE_COUNT);
......
_dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup);
_dispatch_thread_key_create(&dispatch_sema4_key,
(void (*)(void *))_dispatch_thread_semaphore_dispose);
_dispatch_thread_key_create(&dispatch_cache_key, _dispatch_cache_cleanup);
_dispatch_thread_key_create(&dispatch_io_key, NULL);
_dispatch_thread_key_create(&dispatch_apply_key, NULL);
#if DISPATCH_PERF_MON
_dispatch_thread_key_create(&dispatch_bcounter_key, NULL);
#endif
......
}
基于大概率使用的派發(fā)任務(wù)拌喉,libdispatch
緩存了dispatch_continuation_s
,采用復(fù)用模式的做法在每次async
中嘗試去獲取空閑的continuation
變量俐银,通過兩個函數(shù)存取數(shù)據(jù):
void* _Nullable pthread_getspecific(pthread_key_t);
int pthread_setspecific(pthread_key_t , const void * _Nullable);