多线程底层探索
源码中的死锁
当代码运行到这里时线程会死锁,接下来从源码观察
通过汇编看到运行以后会来到这个方法__DISPATCH_WAIT_FOR_QUEUE__,然后打开源码
然后再进一步看看_dq_state_drain_locked_by源码
DISPATCH_ALWAYS_INLINE
static inline bool
_dq_state_drain_locked_by(uint64_t dq_state, dispatch_tid tid)
{
return _dispatch_lock_is_locked_by((dispatch_lock)dq_state, tid);
}
直接返回了_dispatch_lock_is_locked_by,参数是线程的id继续查看
DISPATCH_ALWAYS_INLINE
static inline bool
_dispatch_lock_is_locked_by(dispatch_lock lock_value, dispatch_tid tid)
{
// equivalent to _dispatch_lock_owner(lock_value) == tid
return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}
查看DLOCK_OWNER_MASK宏定义为:#define DLOCK_OWNER_MASK ((dispatch_lock)0xfffffffc)
知道DLOCK_OWNER_MASK是一个比较大的数,只有在tid & DLOCK_OWNER_MASK为0的时候这里才会返回yes
因此得出结论:
然后验证:
果然如此。
函数-同步函数和异步函数
同步函数:立即执行,堵塞当前线程 不具备开辟线程的能力
异步函数:可以开辟子线程-不会立即执行-不会堵塞主线程
同步函数的实现:
DISPATCH_NOINLINE
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);
}
unlikely :是系统级别小概率发生的事件
_dispatch_Block_invoke: 是block执行的代码
宏:
#define _dispatch_Block_invoke(bb)
((dispatch_function_t)((struct Block_layout *)bb)->invoke)
从这里可以看出block的本质还是一个Block_layout的结构体,然后实际上执行的是invoke指针指向的代码块
接下来通过查看_dispatch_sync_f源码看看_dispatch_Block_invoke什么时候被执行。
DISPATCH_NOINLINE
static void
_dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
uintptr_t dc_flags)
{
_dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
}
DISPATCH_ALWAYS_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)));
}
没有保存任务马上执行了。
异步函数:
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);
}
static inline dispatch_qos_t
_dispatch_continuation_init(dispatch_continuation_t dc,
dispatch_queue_class_t dqu, dispatch_block_t work,
dispatch_block_flags_t flags, uintptr_t dc_flags)
{
void *ctxt = _dispatch_Block_copy(work);
dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
if (unlikely(_dispatch_block_has_private_data(work))) {
dc->dc_flags = dc_flags;
dc->dc_ctxt = ctxt;
// will initialize all fields but requires dc_flags & dc_ctxt to be set
return _dispatch_continuation_init_slow(dc, dqu, flags);
}
dispatch_function_t func = _dispatch_Block_invoke(work);
if (dc_flags & DC_FLAG_CONSUME) {
func = _dispatch_call_block_and_release;
}
return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}
DISPATCH_ALWAYS_INLINE
static inline void
_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)。
//#define dx_vtable(x) (&(x)->do_vtable->_os_obj_vtable)。区分类型
}
保存了block— RAC – 函数式编程
线程的相关操作
例题 - 以及答案 解析
可降下面的代码块代码,新建工程直接覆盖到viewcontroller.m的文件里面去
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic ,assign) int num;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self test2];
}
-(void)test1 {
dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
//由于当前线程调度不确定,所以3可能在1-2前也可能在1-2后。但是鉴于开辟线程需要时间所以默认3是最快。可以确定的是0789会跟在3的后面。
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"3");
});
NSLog(@"0");
dispatch_async(queue, ^{
NSLog(@"7");
});
dispatch_async(queue, ^{
NSLog(@"8");
});
dispatch_async(queue, ^{
NSLog(@"9");
});
}
-(void)test2 {
dispatch_queue_t t = dispatch_queue_create("lg", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_sync(t, ^{
NSLog(@"2");//当前在串行队列所以2先于5,由于同步执行所以依据FIFO,先完成2,4再到3
dispatch_async(t, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
//12435
}
- (void)test3 {
self.num = 0;
while (self.num < 100) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num ++;
});
}
NSLog(@"self.num = %d",self.num);
/*
因为while会堵塞线程 所以按照线程的调度来看输出肯定大于等于100*/
}
- (void)test4 {
self.num = 0;
for (int i = 0; i < 100; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num ++;
});
}
NSLog(@"self.num = %d",self.num);
/** for循环不会堵塞线程,所以输出可能是0-100*/
}
-(void)test5 {
dispatch_queue_t t = dispatch_queue_create("lg", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_sync(t, ^{
NSLog(@"2");
dispatch_async(t, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
// 12435
}
-(void)test6 {
dispatch_queue_t t = dispatch_queue_create("lg", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(t, ^{
NSLog(@"2");
dispatch_sync(t, ^{
NSLog(@"3");//因为同步执行导致在当前任务需要先执行完3再到4
});
NSLog(@"4");
});
NSLog(@"5");
//15234
}
@end