多线程(中)

436 阅读3分钟

热身

🌰1.

- (void)MTDemo{

while (self.num < 50) {

dispatch_async(dispatch_get_global_queue(0, 0), ^{

self.num++;

});

}

NSLog(@"end : %d",self.num); 

}
//[7322:6968944] end : 52

这个结果并不是确定的,最终结果self.num>=50 分析: 使用了dispatch_async,异步函数,在while循环中,当self.num++还没有返回的时候,于是又走了while循环。 🌰2.

- (void)KSDemo{

for (int i= 0; i<10000; i++) {

dispatch_async(dispatch_get_global_queue(0, 0), ^{

self.num++;

});

}

NSLog(@"end : %d",self.num); // < 10000

}

输出结果:总是小于10000 分析:for循环,循环9999次,但是由于线程不安全,可能self.num的还没有赋上值,就进行了下一次循环,所以最终结果小于9999

由于线程不安全,上面两个可以使用加锁来保证数据的正确。 eg:


- (void)MTDemo{

NSLock *lock = [[NSLock alloc] init];

while (self.num < 50000) {

[lock lock];

dispatch_async(dispatch_get_global_queue(0, 0), ^{

self.num++;

[lock unlock];

});

}

NSLog(@"mt -end : %d",self.num);

}

GCD源码解析

main_q

我们在main.m中打一个断点。 截屏2021-08-06 上午10.10.26.png 左边可以看到开启了一个Queue:com.apple.main-thread(serial)主线程队列 我们先下载libdispatch源码 搜索com.apple.main-thread

struct dispatch_queue_static_s _dispatch_main_q = {

DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),

#if !DISPATCH_USE_RESOLVERS

.do_targetq = _dispatch_get_default_queue(true),

#endif

.dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |

DISPATCH_QUEUE_ROLE_BASE_ANON,

.dq_label = "com.apple.main-thread",

.dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),

.dq_serialnum = 1,

};

com.apple.main-thread是队列的label。我们都知道主队列是串行队列,那么这里面哪里标志它是串行队列呢?是.dq_serialnum = 1,吗? 要分析是什么标志它是一个串行队列,我们就要从队列的创建入手,看创建的时候哪些赋值可以说明它是一个串行队列。

dispatch_queue_t

dispatch_queue_create(const char * _Nullable label,

dispatch_queue_attr_t _Nullable attr);

队列创建的api中,第一个参数是label,返回值为dispatch_queue_t 在源码中搜索dispatch_queue_create(con

dispatch_queue_t

dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)

{

return _dispatch_lane_create_with_target(label, attr,

DISPATCH_TARGET_QUEUE_DEFAULT, true);

}

我们看下_dispatch_lane_create_with_target

static dispatch_queue_t

_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,

dispatch_queue_t tq, bool legacy)

{

dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

// Step 1: Normalize arguments (qos, overcommit, tq)

dispatch_qos_t qos = dqai.dqai_qos;

#if !HAVE_PTHREAD_WORKQUEUE_QOS

if (qos == DISPATCH_QOS_USER_INTERACTIVE) {

dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;

}

if (qos == DISPATCH_QOS_MAINTENANCE) {

dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;

}

#endif // !HAVE_PTHREAD_WORKQUEUE_QOS

_dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;

if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {

if (tq->do_targetq) {

DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "

"a non-global target queue");

}

}

...


// Step 2: Initialize the queue

...

const void *vtable;

dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;

if (dqai.dqai_concurrent) {

vtable = DISPATCH_VTABLE(queue_concurrent);

} else {

vtable = DISPATCH_VTABLE(queue_serial);

}
...

dispatch_lane_t dq = _dispatch_object_alloc(vtable,

sizeof(struct dispatch_lane_s));

_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?

DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |

(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));



dq->dq_label = label;

dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,

dqai.dqai_relpri);

if (overcommit == _dispatch_queue_attr_overcommit_enabled) {

dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;

}

if (!dqai.dqai_inactive) {

_dispatch_queue_priority_inherit_from_target(dq, tq);

_dispatch_lane_inherit_wlh_from_target(dq, tq);

}

_dispatch_retain(tq);

dq->do_targetq = tq;

_dispatch_object_debug(dq, "%s", __func__);

return _dispatch_trace_queue_create(dq)._dq;

}

我们先来看返回值_dispatch_trace_queue_create(dq)._dq, dq为重点,我们看到

dispatch_lane_t dq = _dispatch_object_alloc(vtable,

sizeof(struct dispatch_lane_s));

_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?

DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |

(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

_dispatch_queue_init 当为串行队列时,_dispatch_queue_init的参数为(dq, dqf, 1, DISPATCH_QUEUE_ROLE_INNER | (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); 我们看下_dispatch_queue_init这个函数

static inline dispatch_queue_class_t

_dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf,

uint16_t width, uint64_t initial_state_bits)

{

uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);

dispatch_queue_t dq = dqu._dq;



dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK |

DISPATCH_QUEUE_INACTIVE)) == 0);


if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) {

dq->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume

if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {

dq->do_ref_cnt++; // released when DSF_DELETED is set

}

}


dq_state |= initial_state_bits;

dq->do_next = DISPATCH_OBJECT_LISTLESS;

dqf |= DQF_WIDTH(width);

os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);

dq->dq_state = dq_state;

dq->dq_serialnum =

os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);

return dqu;

}

此时第三个参数width为1. 我们看下`dq->dq_serialnum =

os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);,_dispatch_queue_serial_numbers`的定义是

`unsigned long volatile _dispatch_queue_serial_numbers =

DISPATCH_QUEUE_SERIAL_NUMBER_INIT;`

// skip zero

// 1 - main_q

// 2 - mgr_q

// 3 - mgr_root_q

// 4,5,6,7,8,9,10,11,12,13,14,15 - global queues

// 17 - workloop_fallback_q

// we use 'xadd' on Intel, so the initial value == next assigned

#define DISPATCH_QUEUE_SERIAL_NUMBER_INIT 17

1代表main_q,4-15带表global queues os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);是一个标记

对于dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)

{

dispatch_queue_attr_info_t dqai = { };


if (!dqa) return dqai;
...
}

所以我们用dispatch_queue_create来创建队列的时候第二个参数可以传NULL

dispatch_queue_t

截屏2021-08-06 上午9.50.05.png

搜索DISPATCH_DECL

#define DISPATCH_DECL(name) \

typedef struct name##_s : public dispatch_object_s {} *name##_t

即 typedef struct dispatch_queue_s: public dispatch_object_s {} *dispatch_queue_t dispatch_queue_t是一个dispatch_object_s类型的

struct dispatch_object_s {

_DISPATCH_OBJECT_HEADER(object);

};

_DISPATCH_OBJECT_HEADER

#define _DISPATCH_OBJECT_HEADER(x) \

struct _os_object_s _as_os_obj[0]; \

OS_OBJECT_STRUCT_HEADER(dispatch_##x); \

struct dispatch_##x##_s *volatile do_next; \

struct dispatch_queue_s *do_targetq; \

void *do_ctxt; \

union { \

dispatch_function_t DISPATCH_FUNCTION_POINTER do_finalizer; \

void *do_introspection_ctxt; \

}

_os_object_s

typedef struct _os_object_s {

_OS_OBJECT_HEADER(

const _os_object_vtable_s *__ptrauth_objc_isa_pointer os_obj_isa,

os_obj_ref_cnt,

os_obj_xref_cnt);

} _os_object_s;
同步函数

研究对象

dispatch_sync(dispatch_get_global_queue(0, 0), ^{

        NSLog(@"测试巴拉巴拉巴拉");

    });

搜索源码,dispatch_sync(dis

void

dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)

{

uintptr_t dc_flags = DC_FLAG_BLOCK;

if (unlikely(_dispatch_block_has_private_data(work))) {

return _dispatch_sync_block_with_privdata(dq, work, dc_flags);

}

_dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);

}

传来的block为dispatch_block_t work _dispatch_Block_invoke(work)源码如下,block的调用

#define _dispatch_Block_invoke(bb) 

((dispatch_function_t)((struct Block_layout *)bb)->invoke)

_dispatch_sync_f调用了_dispatch_sync_f_inline,_dispatch_sync_f_inline的实现(主要代码)

static inline void

_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,

dispatch_function_t func, uintptr_t dc_flags)

{

if (likely(dq->dq_width == 1)) {

return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);

}



if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {

DISPATCH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");

}


dispatch_lane_t dl = upcast(dq)._dl;

// Global concurrent queues and queues bound to non-dispatch threads

// always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE

if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {

return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);

}


if (unlikely(dq->do_targetq->do_targetq)) {

return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);

}

_dispatch_introspection_sync_begin(dl);

_dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(

_dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));

}

func就是那个函数,但是我们看到有好几次调用,到底走哪个分支呢,我们加个符号断点 截屏2021-08-06 上午11.31.34.png

截屏2021-08-06 上午11.32.59.png 走的是_dispatch_sync_f_slow


_dispatch_sync_f_slow(dispatch_queue_class_t top_dqu, void *ctxt,

dispatch_function_t func, uintptr_t top_dc_flags,

dispatch_queue_class_t dqu, uintptr_t dc_flags)

{

dispatch_queue_t top_dq = top_dqu._dq;

dispatch_queue_t dq = dqu._dq;

if (unlikely(!dq->do_targetq)) {

return _dispatch_sync_function_invoke(dq, ctxt, func);

}


pthread_priority_t pp = _dispatch_get_priority();

struct dispatch_sync_context_s dsc = {

.dc_flags = DC_FLAG_SYNC_WAITER | dc_flags,

.dc_func = _dispatch_async_and_wait_invoke,

.dc_ctxt = &dsc,

.dc_other = top_dq,

.dc_priority = pp | _PTHREAD_PRIORITY_ENFORCE_FLAG,

.dc_voucher = _voucher_get(),

.dsc_func = func,

.dsc_ctxt = ctxt,

.dsc_waiter = _dispatch_tid_self(),

};


_dispatch_trace_item_push(top_dq, &dsc);

__DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);


if (dsc.dsc_func == NULL) {

// dsc_func being cleared means that the block ran on another thread ie.

// case (2) as listed in _dispatch_async_and_wait_f_slow.

dispatch_queue_t stop_dq = dsc.dc_other;

return _dispatch_sync_complete_recurse(top_dq, stop_dq, top_dc_flags);

}

_dispatch_introspection_sync_begin(top_dq);

_dispatch_trace_item_pop(top_dq, &dsc);

_dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags

DISPATCH_TRACE_ARG(&dsc));

}

同理,使用符号断点走的是_dispatch_sync_function_invoke

static void

_dispatch_sync_function_invoke(dispatch_queue_class_t dq, void *ctxt,

dispatch_function_t func)

{

_dispatch_sync_function_invoke_inline(dq, ctxt, func);

}

_dispatch_sync_function_invoke_inline

static inline void

_dispatch_sync_function_invoke_inline(dispatch_queue_class_t dq, void *ctxt,

dispatch_function_t func)

{

dispatch_thread_frame_s dtf;

_dispatch_thread_frame_push(&dtf, dq);

_dispatch_client_callout(ctxt, func);

_dispatch_perfmon_workitem_inc();

_dispatch_thread_frame_pop(&dtf);

}

_dispatch_client_callout

void

_dispatch_client_callout(void *ctxt, dispatch_function_t f)

{

_dispatch_get_tsd_base();

void *u = _dispatch_get_unwind_tsd();

if (likely(!u)) return f(ctxt);

_dispatch_set_unwind_tsd(NULL);

f(ctxt);

_dispatch_free_unwind_tsd();

_dispatch_set_unwind_tsd(u);

}

f(ctxt);调用block

异步函数调用

我们也可以用bt的方式来打印堆栈调用

截屏2021-08-06 下午1.12.50.png 异步函数的调用取决于cpu的调度,我们看下源码

void

dispatch_async(dispatch_queue_t dq, dispatch_block_t work)

{

dispatch_continuation_t dc = _dispatch_continuation_alloc();

uintptr_t dc_flags = DC_FLAG_CONSUME;

dispatch_qos_t qos;

qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);

_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);

}

_dispatch_continuation_init中调用了_dispatch_continuation_init_f

static inline dispatch_qos_t

_dispatch_continuation_init_f(dispatch_continuation_t dc,

dispatch_queue_class_t dqu, void *ctxt, dispatch_function_t f,

dispatch_block_flags_t flags, uintptr_t dc_flags)

{

pthread_priority_t pp = 0;

dc->dc_flags = dc_flags | DC_FLAG_ALLOCATED;

dc->dc_func = f;

dc->dc_ctxt = ctxt;

// in this context DISPATCH_BLOCK_HAS_PRIORITY means that the priority

// should not be propagated, only taken from the handler if it has one

if (!(flags & DISPATCH_BLOCK_HAS_PRIORITY)) {

pp = _dispatch_priority_propagate();

}

_dispatch_continuation_voucher_set(dc, flags);

return _dispatch_continuation_priority_set(dc, dqu, pp, flags);

}

_dispatch_continuation_init_f中做了一些存储和优先级的设置 重点看下_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);

_dispatch_continuation_async(dispatch_queue_class_t dqu,

dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)

{

#if DISPATCH_INTROSPECTION

if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {

_dispatch_trace_item_push(dqu, dc);

}

#else

(void)dc_flags;

#endif

return dx_push(dqu._dq, dc, qos);

}

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)是一个宏定义,由于我们找到是block的调用,所以只关心z这个参数就可以了,从这个宏定义看,dx_push可能会存在赋值 我们搜索dx_push

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,

.do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,

.do_dispose     = _dispatch_object_no_dispose,

.do_debug       = _dispatch_queue_debug,

.do_invoke      = _dispatch_object_no_invoke,

.dq_activate    = _dispatch_queue_no_activate,

.dq_wakeup      = _dispatch_root_queue_wakeup,

.dq_push        = _dispatch_root_queue_push,

);

搜索_dispatch_root_queue_push

static inline void

_dispatch_root_queue_push_inline(dispatch_queue_global_t dq,

dispatch_object_t _head, dispatch_object_t _tail, int n)

{

struct dispatch_object_s *hd = _head._do, *tl = _tail._do;

if (unlikely(os_mpsc_push_list(os_mpsc(dq, dq_items), hd, tl, do_next))) {

return _dispatch_root_queue_poke(dq, n, 0);

}

}

_dispatch_root_queue_poke中中调用了_dispatch_root_queue_poke_slow

_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)

{

int remaining = n;

#if !defined(_WIN32)

int r = ENOSYS;

#endif

_dispatch_root_queues_init();

_dispatch_debug_root_queue(dq, __func__);

_dispatch_trace_runtime_event(worker_request, dq, (uint64_t)n);
...
}

我们看下_dispatch_root_queues_init

static inline void

_dispatch_root_queues_init(void)

{

dispatch_once_f(&_dispatch_root_queues_pred, NULL,

_dispatch_root_queues_init_once);

}
static void

_dispatch_root_queues_init_once(void *context DISPATCH_UNUSED)

{

_dispatch_fork_becomes_unsafe();

#if DISPATCH_USE_INTERNAL_WORKQUEUE

size_t i;

for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) {

_dispatch_root_queue_init_pthread_pool(&_dispatch_root_queues[i], 0,

_dispatch_root_queues[i].dq_priority);

}

#else

int wq_supported = _pthread_workqueue_supported();

int r = ENOTSUP;

if (!(wq_supported & WORKQ_FEATURE_MAINTENANCE)) {

DISPATCH_INTERNAL_CRASH(wq_supported,

"QoS Maintenance support required");

}

#if DISPATCH_USE_KEVENT_SETUP

struct pthread_workqueue_config cfg = {

.version = PTHREAD_WORKQUEUE_CONFIG_VERSION,

.flags = 0,

.workq_cb = 0,

.kevent_cb = 0,

.workloop_cb = 0,

.queue_serialno_offs = dispatch_queue_offsets.dqo_serialnum,

#if PTHREAD_WORKQUEUE_CONFIG_VERSION >= 2

.queue_label_offs = dispatch_queue_offsets.dqo_label,

#endif

};

#endif

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wunreachable-code"

if (unlikely(!_dispatch_kevent_workqueue_enabled)) {

#if DISPATCH_USE_KEVENT_SETUP

cfg.workq_cb = _dispatch_worker_thread2;

r = pthread_workqueue_setup(&cfg, sizeof(cfg));

#else

r = _pthread_workqueue_init(_dispatch_worker_thread2,

offsetof(struct dispatch_queue_s, dq_serialnum), 0);

#endif // DISPATCH_USE_KEVENT_SETUP

#if DISPATCH_USE_KEVENT_WORKLOOP

} else if (wq_supported & WORKQ_FEATURE_WORKLOOP) {

#if DISPATCH_USE_KEVENT_SETUP

cfg.workq_cb = _dispatch_worker_thread2;
...
}

可以看到cfg.workq_cb = _dispatch_worker_thread2; 就是前面堆栈信息中的_dispatch_worker_thread2 由堆栈信息看出,_dispatch_worker_thread2是由系统调用的,然后经过

  • libdispatch.dylib_dispatch_worker_thread2
  • libdispatch.dylib_dispatch_root_queue_drain
  • ibdispatch.dylib_dispatch_queue_override_invoke
  • libdispatch.dylib_dispatch_client_callout

触发block的执行。