iOS底层-多线程、GCD分析、锁、runloop

背景

  • 我们一直玩着线程,那多线程原理?
  • GCD使用及坑点?
  • GCD原理-libdispatch-队列-线程-同步/异步-栅栏函数
  • 为解决多线程资源抢夺的问题,怎么加锁呢?
  • 大民哥带你探索多线程、GCD以及锁的原理

准备

  • 可编译的objc4工程
  • libdispatch源码
  • swift-corelibs-foundationSwift的foundation源码
  • foundation源码

线程

一般开辟线程内存最小为16KB,默认开辟线程内存为512KB,其中主线程默认1MB,对于1GB来说最多可开辟64 * 1024个线程

线程和进程定义

  • 进程是指在系统中正在运⾏的⼀个应⽤程序
    每个进程之间是独⽴的,每个进程均运⾏在其专⽤的且受保护的内存空间内
    通过“活动监视器”可以查看 Mac 系统中所开启的进程
  • 线程是进程的基本执⾏单元,⼀个进程的所有任务都在线程中执⾏
    进程要想执⾏任务,必须得有线程,进程⾄少要有⼀条线程
    程序启动会默认开启⼀条线程,这条线程被称为主线程或 UI 线程
  • 地址空间:同⼀进程的线程共享本进程的地址空间,⽽进程之间则是独⽴的地址空间。
  • 资源拥有:同⼀进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独⽴的。

1: ⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进
程都死掉。所以多进程要⽐多线程健壮。
2: 进程切换时,消耗的资源⼤,效率⾼。所以涉及到频繁的切换时,使⽤线程要好于进
程。同样如果要求同时进⾏并且⼜要共享某些变量的并发操作,只能⽤线程不能⽤进程
3: 执⾏过程:每个独⽴的进程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序⼊⼝。但是
线程不能独⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制。
4: 线程是处理器调度的基本单位,但是进程不是。
5: 线程没有地址空间,线程包含在进程地址空间中

多线程优缺点

(操作系统书籍,“看书”)

  • 优点
    1,能适当提⾼程序的执⾏效率
    2,能适当提⾼资源的利⽤率(CPU,内存)
    3,线程上的任务执⾏完成后,线程会⾃动销毁
  • 缺点
    1,开启线程需要占⽤⼀定的内存空间(默认情况下,每⼀个线程都占 512 KB)
    2, 如果开启⼤量的线程,会占⽤⼤量的内存空间,降低程序的性能
    3,线程越多,CPU 在调⽤线程上的开销就越⼤
    4,程序设计更加复杂,⽐如线程间的通信、多线程的数据共享

线程调度

时间⽚的概念:CPU在多个任务直接进⾏快速的切换,这个时间间隔就是时间⽚

  • (单核CPU)同⼀时间,CPU 只能处理 1 个线程
  • 换⾔之,同⼀时间只有 1 个线程在执⾏
  • 多线程同时执⾏:
  • CPU 快速的在多个线程之间的切换
  • CPU 调度线程的时间⾜够快,就造成了多线程的“同时”执⾏的效果
  • 如果线程数⾮常多
  • CPU 会在 N 个线程之间切换,消耗⼤量的 CPU 资源
  • 每个线程被调度的次数会降低,线程的执⾏效率降低
  • 线程是执行任务的基本单元

(多核是解决高并发的根本,但iOS来说一直是单核)
在这里插入图片描述
在这里插入图片描述

同步/异步

  • 能否开辟线程,同步不能开辟线程,异步则可
  • 任务的回调是否具备异步性和同步性

队列

  • 串行和并发
    在这里插入图片描述

  • 串型队列:FIFO, dispatch_queue_t serial = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);

  • 并发队列:CPU的调度, dispatch_queue_t conque = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);

#pragma mark - 队列函数的应用

- (void)textDemo2{
    // 同步队列
    dispatch_queue_t queue = dispatch_queue_create("cooci", NULL);
    NSLog(@"1");
    // 异步函数
    dispatch_async(queue, ^{
        NSLog(@"2");
        // 同步
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        // NSLog(@"4");
    });
    NSLog(@"5");
    
    // 1 5 2 死锁
    //

}

- (void)textDemo1{
    
    dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");

}

死锁

dispatch_sync_f_slow

在这里插入图片描述

案例

请添加图片描述
堵塞的是整个dispatch_async();,需要外层的dispatch_async()走完,块与块的循环死锁

主线程因为你同步函数的原因等着先执⾏任务
主队列等着主线程的任务执⾏完毕再执⾏⾃⼰的任务
主队列和主线程相互等待会造成死锁

多线程原理

线程生命周期:

在这里插入图片描述
在这里插入图片描述

线程池

在这里插入图片描述

  • 1,先判断线程池的线程是否都有任务,如果线程池工作队列饱满都处于执行状态,就交给饱和策略去处理(直接丢弃任务,淘汰超时任务,报异常(超负载), 任务退回到调度者),

饱和策略

这四种拒绝策略均实现的RejectedExecutionHandler接⼝:

  • AbortPolicy 直接抛出RejectedExecutionExeception异常来阻⽌系统正常运⾏
  • CallerRunsPolicy 将任务回退到调⽤者
  • DisOldestPolicy 丢掉等待最久的任务
  • DisCardPolicy 直接丢弃任务

任务执行速度影响

  • CPU调度速度
  • 任务复杂度
  • 任务优先级
  • 线程状态

优先级翻转

  • IO密集型 频繁等待
  • CPU密集型 很少等待 优先级高

当IO频繁等待时,CPU开始调度提升IO优先级,提升优先级的因素:
1,用户指定优先级
2,进入等待的频繁程度
3,长时间不执行会提升优先级

atomic与nonatomic 的区别

  • iOS 开发的建议
    1,所有属性都声明为 nonatomic
    2,尽量避免多线程抢夺同⼀块资源
    3,尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减⼩移动客户端的压⼒
  • nonatomic:⾮原⼦属性,⾮线程安全,适合内存⼩的移动设备
  • atomic:原⼦属性(线程安全),针对多线程设计的,默认值,需要消耗⼤量的资源
    单写多读:单个线程写⼊,多个线程可以读取
    atomic 本身只是标识符,内部底层逻辑就有⼀把锁(⾃旋锁)
    保证同⼀时间只有⼀个线程能够写⼊(但是同⼀个时间多个线程都可以取值)

GCD分析

GCD 简介

  • 什么是GCD?
    全称是 Grand Central Dispatch
    纯 C 语⾔,提供了⾮常多强⼤的函数
  • GCD的优势
    1,GCD 是苹果公司为多核的并⾏运算提出的解决⽅案
    2,GCD 会⾃动利⽤更多的CPU内核(⽐如双核、四核)
    3,GCD 会⾃动管理线程的⽣命周期(创建线程、调度任务、销毁线程)
    4,程序员只需要告诉 GCD 想要执⾏什么任务,不需要编写任何线程管理代码,将任务添加到队列,并且指定执⾏任务的函数

GCD探索

根据打印:

<OS_dispatch_queue_serial: cooci>-<OS_dispatch_queue_concurrent: cooci>-<OS_dispatch_queue_main: com.apple.main-thread>-<OS_dispatch_queue_global: com.apple.root.default-qos>
  • 探索libdispatch源码:

主队列探索(com.apple.main-thread)

在这里插入图片描述

dispatch_queue_static_s
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,
};
  • .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),表明dispatch_get_main_queue()主队列为串型队列。

串型队列特性

  • 首先要知道串型和并发的区别在创建的时候dispatch_queue_create的区别,那么我们需要探索dispatch_queue_create
    在这里插入图片描述
_dispatch_lane_create_with_target

进入到_dispatch_lane_create_with_target方法,核心方法,创建和初始化:
在这里插入图片描述

_dispatch_queue_init

进入到_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;
}

在这里插入图片描述
里面dqf |= DQF_WIDTH(width);表明主队列为为串型队列。

在这里插入图片描述
dq_serialnum,_dispatch_queue_serial_numbers为标识number。

  • 探索_dispatch_queue_serial_numbers:
    其宏定义为17
// 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
extern unsigned long volatile _dispatch_queue_serial_numbers;

代表创建的队列号number。

  • 探索os_atomic_inc_orig:
#define os_atomic_inc_orig(p, m) \
		os_atomic_add_orig((p), 1, m)

#define os_atomic_add_orig(p, v, m) \
		_os_atomic_c11_op_orig((p), (v), m, add, +)
		
#define _os_atomic_c11_op_orig(p, v, m, o, op) \
atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), v, \
		memory_order_##m)

那么为什么dispatch创建却是_dispatch_object_alloc,为什么不是_dispatch_queue_alloc呢?代码如下:

dispatch_lane_t dq = _dispatch_object_alloc(vtable,
			sizeof(struct dispatch_lane_s)); // alloc
	_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
			DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
			(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init

_dispatch_object_alloc
  • 探索_dispatch_object_alloc实现:
void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
	const struct dispatch_object_vtable_s *_vtable = vtable;
	dispatch_object_t dou;
	dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
	dou._do->do_vtable = vtable;
	return dou._do;
#else
	return _os_object_alloc_realized(vtable, size);
#endif
}
  • _os_object_alloc_realized
_os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
	dispatch_assert(size >= sizeof(struct _os_object_s));
	return _os_objc_alloc(cls, size);
}
  • _os_objc_alloc
static inline id
_os_objc_alloc(Class cls, size_t size)
{
	id obj;
	size -= sizeof(((struct _os_object_s *)NULL)->os_obj_isa);
	while (unlikely(!(obj = class_createInstance(cls, size)))) {
		_dispatch_temporary_resource_shortage();
	}
	return obj;
}
  • _dispatch_temporary_resource_shortage
void
_dispatch_temporary_resource_shortage(void)
{
	sleep(1);
	__asm__ __volatile__("");  // prevent tailcall
}

全局并发队列(com.apple.root.default-qos)

//其本质为结构体
struct dispatch_queue_global_s _dispatch_root_queues[] = {}
GCD底层源码继承链

请添加图片描述
dispatch_queue_t -> dispatch_queue_s -> dispatch_object_s -> dispatch_object_t

typedef struct dispatch_object_s {
private:
	dispatch_object_s();
	~dispatch_object_s();
	dispatch_object_s(const dispatch_object_s &);
	void operator=(const dispatch_object_s &);
} *dispatch_object_t;
#define DISPATCH_DECL(name) \
		typedef struct name##_s : public dispatch_object_s {} *name##_t
#define DISPATCH_DECL_SUBCLASS(name, base) \
		typedef struct name##_s : public base##_s {} *name##_t
#define DISPATCH_GLOBAL_OBJECT(type, object) (static_cast<type>(&(object)))
#define DISPATCH_RETURNS_RETAINED
#else /* Plain C */
#ifndef __DISPATCH_BUILDING_DISPATCH__
typedef union {
	struct _os_object_s *_os_obj;
	struct dispatch_object_s *_do;
	struct dispatch_queue_s *_dq;
	struct dispatch_queue_attr_s *_dqa;
	struct dispatch_group_s *_dg;
	struct dispatch_source_s *_ds;
	struct dispatch_channel_s *_dch;
	struct dispatch_mach_s *_dm;
	struct dispatch_mach_msg_s *_dmsg;
	struct dispatch_semaphore_s *_dsema;
	struct dispatch_data_s *_ddata;
	struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;
#endif // !__DISPATCH_BUILDING_DISPATCH__
dq_push
DISPATCH_VTABLE_INSTANCE(queue,
	// This is the base class for queues, no objects of this type are made
	.do_type        = _DISPATCH_QUEUE_CLUSTER,
	.do_dispose     = _dispatch_object_no_dispose,
	.do_debug       = _dispatch_queue_debug,
	.do_invoke      = _dispatch_object_no_invoke,

	.dq_activate    = _dispatch_queue_no_activate,
);

DISPATCH_VTABLE_INSTANCE(workloop,
	.do_type        = DISPATCH_WORKLOOP_TYPE,
	.do_dispose     = _dispatch_workloop_dispose,
	.do_debug       = _dispatch_queue_debug,
	.do_invoke      = _dispatch_workloop_invoke,

	.dq_activate    = _dispatch_queue_no_activate,
	.dq_wakeup      = _dispatch_workloop_wakeup,
	.dq_push        = _dispatch_workloop_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
	.do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
	.do_dispose     = _dispatch_lane_dispose,
	.do_debug       = _dispatch_queue_debug,
	.do_invoke      = _dispatch_lane_invoke,

	.dq_activate    = _dispatch_lane_activate,
	.dq_wakeup      = _dispatch_lane_wakeup,
	.dq_push        = _dispatch_lane_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
	.do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
	.do_dispose     = _dispatch_lane_dispose,
	.do_debug       = _dispatch_queue_debug,
	.do_invoke      = _dispatch_lane_invoke,

	.dq_activate    = _dispatch_lane_activate,
	.dq_wakeup      = _dispatch_lane_wakeup,
	.dq_push        = _dispatch_lane_concurrent_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
void
_dispatch_root_queue_push(dispatch_queue_global_t rq, dispatch_object_t dou,
		dispatch_qos_t qos)
{
#if DISPATCH_USE_KEVENT_WORKQUEUE
	dispatch_deferred_items_t ddi = _dispatch_deferred_items_get();
	if (unlikely(ddi && ddi->ddi_can_stash)) {
		dispatch_object_t old_dou = ddi->ddi_stashed_dou;
		dispatch_priority_t rq_overcommit;
		rq_overcommit = rq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT;

		if (likely(!old_dou._do || rq_overcommit)) {
			dispatch_queue_global_t old_rq = ddi->ddi_stashed_rq;
			dispatch_qos_t old_qos = ddi->ddi_stashed_qos;
			ddi->ddi_stashed_rq = rq;
			ddi->ddi_stashed_dou = dou;
			ddi->ddi_stashed_qos = qos;
			_dispatch_debug("deferring item %p, rq %p, qos %d",
					dou._do, rq, qos);
			if (rq_overcommit) {
				ddi->ddi_can_stash = false;
			}
			if (likely(!old_dou._do)) {
				return;
			}
			// push the previously stashed item
			qos = old_qos;
			rq = old_rq;
			dou = old_dou;
		}
	}
#endif
#if HAVE_PTHREAD_WORKQUEUE_QOS
	if (_dispatch_root_queue_push_needs_override(rq, qos)) {
		return _dispatch_root_queue_push_override(rq, dou, qos);
	}
#else
	(void)qos;
#endif
	_dispatch_root_queue_push_inline(rq, dou, dou, 1);
}

进入_dispatch_root_queue_push_inline找其中_dispatch_root_queue_poke方法->_dispatch_root_queue_poke_slow

void
_dispatch_root_queue_poke(dispatch_queue_global_t dq, int n, int floor)
{
	if (!_dispatch_queue_class_probe(dq)) {
		return;
	}
#if !DISPATCH_USE_INTERNAL_WORKQUEUE
#if DISPATCH_USE_PTHREAD_POOL
	if (likely(dx_type(dq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE))
#endif
	{
		if (unlikely(!os_atomic_cmpxchg2o(dq, dgq_pending, 0, n, relaxed))) {
			_dispatch_root_queue_debug("worker thread request still pending "
					"for global queue: %p", dq);
			return;
		}
	}
#endif // !DISPATCH_USE_INTERNAL_WORKQUEUE
	return _dispatch_root_queue_poke_slow(dq, n, floor);
}
  • 进入并发队列:_dispatch_lane_concurrent_push并找到_dispatch_lane_push
void
_dispatch_lane_concurrent_push(dispatch_lane_t dq, dispatch_object_t dou,
		dispatch_qos_t qos)
{
	// <rdar://problem/24738102&24743140> reserving non barrier width
	// doesn't fail if only the ENQUEUED bit is set (unlike its barrier
	// width equivalent), so we have to check that this thread hasn't
	// enqueued anything ahead of this call or we can break ordering
	if (dq->dq_items_tail == NULL &&
			!_dispatch_object_is_waiter(dou) &&
			!_dispatch_object_is_barrier(dou) &&
			_dispatch_queue_try_acquire_async(dq)) {
		return _dispatch_continuation_redirect_push(dq, dou, qos);
	}

	_dispatch_lane_push(dq, dou, qos);
}
  • 找到_dispatch_lane_push里面,其中
  • _dispatch_lane_push_waiter为死锁
  • dx_wakeup是dq_push -> _dispatch_lane_wakeup
void
_dispatch_lane_push(dispatch_lane_t dq, dispatch_object_t dou,
		dispatch_qos_t qos)
{
	dispatch_wakeup_flags_t flags = 0;
	struct dispatch_object_s *prev;

	if (unlikely(_dispatch_object_is_waiter(dou))) {
		return _dispatch_lane_push_waiter(dq, dou._dsc, qos);
	}

	dispatch_assert(!_dispatch_object_is_global(dq));
	qos = _dispatch_queue_push_qos(dq, qos);

	// If we are going to call dx_wakeup(), the queue must be retained before
	// the item we're pushing can be dequeued, which means:
	// - before we exchange the tail if we have to override
	// - before we set the head if we made the queue non empty.
	// Otherwise, if preempted between one of these and the call to dx_wakeup()
	// the blocks submitted to the queue may release the last reference to the
	// queue when invoked by _dispatch_lane_drain. <rdar://problem/6932776>

	prev = os_mpsc_push_update_tail(os_mpsc(dq, dq_items), dou._do, do_next);
	if (unlikely(os_mpsc_push_was_empty(prev))) {
		_dispatch_retain_2_unsafe(dq);
		flags = DISPATCH_WAKEUP_CONSUME_2 | DISPATCH_WAKEUP_MAKE_DIRTY;
	} else if (unlikely(_dispatch_queue_need_override(dq, qos))) {
		// There's a race here, _dispatch_queue_need_override may read a stale
		// dq_state value.
		//
		// If it's a stale load from the same drain streak, given that
		// the max qos is monotonic, too old a read can only cause an
		// unnecessary attempt at overriding which is harmless.
		//
		// We'll assume here that a stale load from an a previous drain streak
		// never happens in practice.
		_dispatch_retain_2_unsafe(dq);
		flags = DISPATCH_WAKEUP_CONSUME_2;
	}
	os_mpsc_push_update_prev(os_mpsc(dq, dq_items), prev, dou._do, do_next);
	if (flags) {
		return dx_wakeup(dq, qos, flags);
	}
}
void
_dispatch_lane_wakeup(dispatch_lane_class_t dqu, dispatch_qos_t qos,
		dispatch_wakeup_flags_t flags)
{
	dispatch_queue_wakeup_target_t target = DISPATCH_QUEUE_WAKEUP_NONE;

	if (unlikely(flags & DISPATCH_WAKEUP_BARRIER_COMPLETE)) {
		return _dispatch_lane_barrier_complete(dqu, qos, flags);
	}
	if (_dispatch_queue_class_probe(dqu)) {
		target = DISPATCH_QUEUE_WAKEUP_TARGET;
	}
	return _dispatch_queue_wakeup(dqu, qos, flags, target);
}
  • 然后到_dispatch_queue_wakeup
    在这里插入图片描述
  • 后到,_dispatch_lane_class_barrier_complete

异步并发堆栈流程:

dispatch_queue_t conque = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(conque, ^{
     NSLog(@"12334");
});
    frame #1: 0x0000000106c28848 libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #2: 0x0000000106c29a2c libdispatch.dylib`_dispatch_client_callout + 8
    frame #3: 0x0000000106c2c339 libdispatch.dylib`_dispatch_continuation_pop + 594
    frame #4: 0x0000000106c2b708 libdispatch.dylib`_dispatch_async_redirect_invoke + 778
    frame #5: 0x0000000106c3ba09 libdispatch.dylib`_dispatch_root_queue_drain + 419
    frame #6: 0x0000000106c3c4c3 libdispatch.dylib`_dispatch_worker_thread2 + 196
    frame #7: 0x00007fff6da28417 libsystem_pthread.dylib`_pthread_wqthread + 244
    frame #8: 0x00007fff6da2742f libsystem_pthread.dylib`start_wqthread + 15

单例底层原理

  • 待后续有时间再补充

栅栏函数

  • 最直接的作⽤: 控制任务执⾏顺序,同步
  • dispatch_barrier_async 前⾯的任务执⾏完毕才会来到这⾥
  • dispatch_barrier_sync 作⽤相同,但是这个会堵塞线程,影响后⾯的任务执⾏
  • ⾮常重要的⼀点:
    1, 栅栏函数只能控制同⼀自定义的并发队列
    2,dispatch_get_global_queue(0, 0)全局并发队列中栅栏函数无效
- (void)demoBarrier{
//    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);//全局并发
    dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);//并发
    /* 1.异步函数 */
    dispatch_async(concurrentQueue, ^{
        sleep(1);
        NSLog(@"123");
        
    });
    
    /* 2. 栅栏函数 */
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"---------------------%@------------------------",[NSThread currentThread]);
    });
    /* 3. 异步函数 */
    dispatch_async(concurrentQueue, ^{
        NSLog(@"加载那么多,喘口气!!!");
    });
    NSLog(@"**********起来干!!");
 
}
栅栏函数底层分析:
  • 通过对同步栅栏函数分析dispatch_barrier_sync
    dispatch_barrier_sync -> _dispatch_barrier_sync_f -> _dispatch_barrier_sync_f_inline -> _dispatch_sync_f_slow -> _dispatch_lane_non_barrier_complete -> _dispatch_lane_non_barrier_complete_finish
    在这里插入图片描述
    其中:DC_FLAG_BARRIER处理

主要方法:_dispatch_lane_class_barrier_complete函数:
在这里插入图片描述

  • 通过对异步栅栏函数执行打印bt分析:
    在这里插入图片描述
 frame #1: 0x000000010bd3f848 libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #2: 0x000000010bd40a2c libdispatch.dylib`_dispatch_client_callout + 8
    frame #3: 0x000000010bd5188b libdispatch.dylib`_dispatch_lane_concurrent_drain + 1252
    frame #4: 0x000000010bd48179 libdispatch.dylib`_dispatch_lane_invoke + 625
    frame #5: 0x000000010bd42c71 libdispatch.dylib`_dispatch_queue_override_invoke + 584
    frame #6: 0x000000010bd52a09 libdispatch.dylib`_dispatch_root_queue_drain + 419
    frame #7: 0x000000010bd534c3 libdispatch.dylib`_dispatch_worker_thread2 + 196
    frame #8: 0x00007fff6da28417 libsystem_pthread.dylib`_pthread_wqthread + 244
    frame #9: 0x00007fff6da2742f libsystem_pthread.dylib`start_wqthread + 15
  • 我们查看libdispatch底层源码,找到dispatch_barrier_async函数:
#ifdef __BLOCKS__
void
dispatch_barrier_async(dispatch_queue_t dq, dispatch_block_t work)
{
	dispatch_continuation_t dc = _dispatch_continuation_alloc();
	uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_BARRIER;
	dispatch_qos_t qos;

	qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
	_dispatch_continuation_async(dq, dc, qos, dc_flags);
}
#endif
  • 看到创建dc,并初始化qos,然后执行_dispatch_continuation_async函数:
    在这里插入图片描述
  • 然后到dx_push在上面我们探索到dx_push跟dq_push有关

调度组

通过上面栅栏函数发现它只能在同一个自定义的并发队列中使用,但实际场景中,我们可能同时开辟了多个并发队列,那栅栏函数就无效了,为了解决这种问题,调度组就诞生了:

  • 最直接的作⽤: 控制任务执⾏顺序
  • dispatch_group_create 创建组
  • dispatch_group_async 进组任务, 里面包含dispatch_group_enter 以及dispatch_group_leave
  • dispatch_group_notify 进组任务执⾏完毕通知
  • dispatch_group_wait 进组任务执⾏等待时间

dispatch_group_enter 进组
dispatch_group_leave 出组
注意搭配使⽤

源码实现案例
- (void)groupDemo2{
    //创建调度组
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    //调度进组任务 -里面包含:dispatch_group_enter 以及dispatch_group_leave
    dispatch_group_async(group, queue, ^{
        NSString *logoStr1 = @"https://f12.baidu.com/it/u=711217113,818398466&fm=72";
        NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
        UIImage *image1 = [UIImage imageWithData:data1];
        [self.mArray addObject:image1];
        sleep(4);
    });
    
//    dispatch_async(queue, ^{
//        NSString *logoStr1 = @"https://f12.baidu.com/it/u=3172787957,1000491180&fm=72";
//        NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
//        UIImage *image1 = [UIImage imageWithData:data1];
//        [self.mArray addObject:image1];
        dispatch_group_leave(group);
//    });
    
    dispatch_group_enter(group);

    //进组任务执行完成回调
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        UIImage *newImage = nil;
        NSLog(@"数组个数:%ld",self.mArray.count);
        for (int i = 0; i<self.mArray.count; i++) {
            UIImage *waterImage = self.mArray[i];
            newImage =[KC_ImageTool kc_WaterImageWithWaterImage:waterImage backImage:newImage waterImageRect:CGRectMake(20, 100*(i+1), 100, 40)];
        }
    });
    dispatch_group_leave(group);

}

信号量

信号量同步执行,可以当锁使用,可以控制并发最大数量。

  • 信号量dispatch_semaphore_t
  • dispatch_semaphore_create 创建信号量并控制每次通过个数,当两个线程需要协调时,值传递零非常有用,特定事件的完成。传递大于零的值是用于管理有限的资源池,其中池大小与值相等。
  • dispatch_semaphore_wait 信号量等待
  • dispatch_semaphore_signal 信号量释放

同步->当锁, 控制GCD最⼤并发数

底层源码探索
  • dispatch_semaphore_wait在底层是个do…while循环,
    如果是DISPATCH_TIME_NOW会超时处理,
    DISPATCH_TIME_FOREVER会等待任务
    do…while循环一直减,直到value 等于 0
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
	long value = os_atomic_dec2o(dsema, dsema_value, acquire);
	if (likely(value >= 0)) {
		return 0;
	}
	return _dispatch_semaphore_wait_slow(dsema, timeout);
}
  • dispatch_semaphore_signal
    -> _dispatch_semaphore_signal_slow

do…while循环一直加,直到value 等于 0

long
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
	long value = os_atomic_inc2o(dsema, dsema_value, release);
	if (likely(value > 0)) {
		return 0;
	}
	if (unlikely(value == LONG_MIN)) {
		DISPATCH_CLIENT_CRASH(value,
				"Unbalanced call to dispatch_semaphore_signal()");
	}
	return _dispatch_semaphore_signal_slow(dsema);
}
源码实现案例
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_semaphore_t sem = dispatch_semaphore_create(3);
    dispatch_queue_t queue1 = dispatch_queue_create("likeIos", NULL);
    //任务1
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        NSLog(@"执行任务1");
        sleep(1);
        NSLog(@"任务1完成");
        dispatch_semaphore_signal(sem);
    });
    
    //任务2
    dispatch_async(queue1, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        NSLog(@"执行任务2");
        sleep(1);
        NSLog(@"任务2完成");
        dispatch_semaphore_signal(sem);

    });
    
    //任务3
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

        NSLog(@"执行任务3");
        sleep(2);
        NSLog(@"任务3完成");
        dispatch_semaphore_signal(sem);

    });
    
    //任务4
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

        NSLog(@"执行任务4");
        sleep(1);
        NSLog(@"任务4完成");
        dispatch_semaphore_signal(sem);

    });

Dispatch_Source

  • 本质:通过条件来控制block执行
  • 其 CPU 负荷⾮常⼩,尽量不占⽤资源
  • 联结的优势
  • 在任⼀线程上调⽤它的的⼀个函数 dispatch_source_merge_data 后,会执⾏
    Dispatch Source 事先定义好的句柄(可以把句柄简单理解为⼀个 block )
    这个过程叫 Custom event ,⽤户事件。是 dispatch source ⽀持处理的⼀种事件
  • 句柄是⼀种指向指针的指针 它指向的就是⼀个类或者结构,它和系统有很密切的关系
    HINSTANCE(实例句柄),HBITMAP(位图句柄),HDC(设备表述句柄),HICON
    (图标句柄)等。这当中还有⼀个通⽤的句柄,就是HANDLE
  • dispatch_source_create 创建源
  • dispatch_source_set_event_handler 设置源事件回调
  • dispatch_source_merge_data 源事件设置数据
  • dispatch_source_get_data 获取源事件数据
  • dispatch_resume 继续
  • dispatch_suspend 挂起

  • 加锁是为了保护线程或者代码执行安全,防止执行中被别的线程插入,而引起资源抢夺

锁的分类

⾃旋锁

  • ⾃旋锁:线程反复检查锁变量是否可⽤。由于线程在这⼀过程中保持执⾏,
    因此是⼀种忙等待。⼀旦获取了⾃旋锁,线程会⼀直保持该锁,直⾄显式释
    放⾃旋锁。 ⾃旋锁避免了进程上下⽂的调度开销,因此对于线程只会阻塞很
    短时间的场合是有效的。

互斥锁

  • 互斥锁:是⼀种⽤于多线程编程中,防⽌两条线程同时对同⼀公共资源(⽐如全局变量)进⾏读写的机制。该⽬的通过将代码切⽚成⼀个⼀个的临界区⽽达成。按照相应顺序(同步)
  • 这⾥属于互斥锁的有:
    • NSLock
    • pthread_mutex
    • @synchronized

条件锁

  • 条件锁:就是条件变量,当进程的某些资源要求不满⾜时就进⼊休眠,也就
    是锁住了。当资源被分配到了,条件锁打开,进程继续运⾏
    • NSCondition
    • NSConditionLock

递归锁

  • 递归锁:就是同⼀个线程可以加锁N次⽽不会引发死锁
    • NSRecursiveLock
    • pthread_mutex(recursive)

信号量

  • 信号量(semaphore):是⼀种更⾼级的同步机制,互斥锁可以说是
    semaphore在仅取值0/1时的特例。信号量可以有更多的取值空间,⽤来实
    现更加复杂的同步,⽽不单单是线程间互斥。
    • dispatch_semaphore

小节总结:

  • 基本的锁就包括了三类 ⾃旋锁 互斥锁 读写锁
  • 条件锁,递归锁,信号量都是上层的封装和实现

总体就两种锁:自旋锁和互斥锁,严格的话自旋锁也是互斥锁

TLS 线程相关解释

线程局部存储(Thread Local Storage,TLS): 是操作系统为线
程单独提供的私有空间,通常只有有限的容量。Linux系统下
通常通过pthread库中的
pthread_key_create()、
pthread_getspecific()、
pthread_setspecific()、
pthread_key_delete()

锁性能分析

通过对锁的性能测试(以100000数据为例),模拟器中数据(同样机型,模拟器数据会比真机数据大一些):
在这里插入图片描述

OSSpinLock: 0.661016 ms
dispatch_semaphore_t: 1.123071 ms
os_unfair_lock_lock: 1.129985 ms
pthread_mutex_t: 1.607060 ms
NSlock: 1.868963 ms
NSCondition: 2.519965 ms
PTHREAD_MUTEX_RECURSIVE: 2.979994 ms
NSRecursiveLock: 5.754113 ms
NSConditionLock: 13.507962 ms
@synchronized: 5.754948 ms

是因为,如果是iPhone而不是模拟器时线程数为8,
⚠️:如果源码可编译,可改一下StripeCount值为1或者0,会绽放不一样的烟火
请添加图片描述

锁的性能数据

在这里插入图片描述

@synchronized

  • 首先@synchronized代码块,然后有枷锁的效果和递归可重用
  • 注意⚠️:
	// 锁的对象 不要为空
    // self 生命周期
    // 方便存储 释放
    // 真机 和 模拟器
    @synchronized (self) {
        //对象不要为空
    }

@synchronized结构

  • 把锁放到main函数文件,通过clang或者xcrun到main.mm
    请添加图片描述
  • 核心在
    objc_sync_enter(_sync_obj);
    objc_sync_exit(_sync_obj);
  • 对这两个方法下符号断点:
    在这里插入图片描述
  • libobjc.A.dylib`objc_sync_enter,看到是在objc里面的objc_sync_enter,那么我们运行objc源码:
    在这里插入图片描述
  • 这个时候如果@synchronized(参数),其中参数传nil时,走objc_sync_nil(); 什么都不会做,那么重点就在关于obj的判断:因此在enter的时候加锁:data->mutex.lock();

对于objc_sync_exit:
在这里插入图片描述

  • 没有data时error,有data时:bool okay = data->mutex.tryUnlock();解锁。

核心代码:SyncData* data = id2data(obj, RELEASE);

SyncData分析

而其中核心SyncData单向链表:
在这里插入图片描述

  • 其中DisguisedPtr:关联对象
    在这里插入图片描述
  • threadCount:递归锁
id2data分析

在这里插入图片描述

  • 其中有线程暂存(TLS)和缓存(cache)两种。
  • 内部有个锁:
    在这里插入图片描述
    在这里插入图片描述
SyncList分析
  • 横向为哈希值一致的对象
    在这里插入图片描述
  • 第一次进入:
    在这里插入图片描述
  • 存储到tls_set_direct,以及cache->list:
    在这里插入图片描述
  • 有值了之后
    在这里插入图片描述
@synchronized总结

synchronized底层源码中有两个重要参数:threadCount(可多线程,表示被多少线程锁过),lockCount(具备可递归,表示同一个线程被锁多少次),多线程可递归
1,TLS保障threadCount多少条线程对这个锁对象加锁,
2,lock++进入多少次

  • sync全局哈希表,采用拉链法,拉链syncData
  • sDatalist array 里面存的synclist(绑定的objc)
  • objc_sync_enter/exit 成对对称,封装底层递归锁
  • 两种存储:tls 和 cache
  • 第一次syncData时 头插法 创建链表结构 并标记threadCount = 1
  • 然后判断是不是同一个对象进来,如果是,在TLS里面lock++, 如果找不到时,会重新创建sync 并threadCount++
  • 否则lock回 threadCount –

NSlock/ NSReLock/NSRecursiveLock分析

  • NSLock案例:
- (void)lg_testNSLock{

    NSLock *lock = [[NSLock alloc] init];
    
    for (int i = 0; i<10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^testMethod)(int);
            testMethod = ^(int value){
                if (value > 0) {
                    sleep(2);
                    NSLog(@"current value = %d",value);
                    testMethod(value - 1);
                }
            };
            [lock lock];
            testMethod(10);
            [lock unlock];
        });
    }
    
    
}

  • @synchronized解决lock的多线程性,NSRecursiveLock没有了lock递归
// @synchronized (self) {}
- (void)lg_testRecursive{

    NSLock *lock = [[NSLock alloc] init];
    
    for (int i = 0; i<10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^testMethod)(int);
            testMethod = ^(int value){
                [self.recursiveLock lock];
                if (value > 0) {
//                    sleep(2);
                    NSLog(@"current value = %d",value);
                    testMethod(value - 1);
                }
                [self.recursiveLock unlock];

            };
            testMethod(10);
        });
    }
    
    
}

NSCondition分析

NSCondition 的对象实际上作为⼀个锁和⼀个线程检查器:锁主要
为了当检测条件时保护数据源,执⾏条件引发的任务;线程检查器
主要是根据条件决定是否继续运⾏线程,即线程是否被阻塞
1:[condition lock];//⼀般⽤于多线程同时访问、修改同⼀个数据源,保证在同⼀
时间内数据源只被访问、修改⼀次,其他线程的命令需要在lock 外等待,只到
unlock ,才可访问
2:[condition unlock];//与lock 同时使⽤
3:[condition wait];//让当前线程处于等待状态
4:[condition signal];//CPU发信号告诉线程不⽤在等待,可以继续执⾏
请添加图片描述

  • 一个生产消费模型,生产安全加锁保护,消费安全加锁保护,当供不应求时需要信号的介入,让消费等待。

NSConditionLock分析

  • 1.1 NSConditionLock 是锁,⼀旦⼀个线程获得锁,其他线程⼀定等待
  • 1.2 [xxxx lock]; 表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的
    condition) 那它能执⾏此⾏以下代码,如果已经有其他线程获得锁(可能是条件锁,或者⽆条件
    锁),则等待,直⾄其他线程解锁
  • 1.3 [xxx lockWhenCondition:A条件]; 表示如果没有其他线程获得该锁,但是该锁内部的
    condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且
    没有其他线程获得该锁,则进⼊代码区,同时设置它获得该锁,其他任何线程都将等待它代码的
    完成,直⾄它解锁。
  • 1.4 [xxx unlockWithCondition:A条件]; 表示释放锁,同时把内部的condition设置为A条件
  • 1.5 return = [xxx lockWhenCondition:A条件 beforeDate:A时间]; 表示如果被锁定(没获得
    锁),并超过该时间则不再阻塞线程。但是注意:返回的值是NO,它没有改变锁的状态,这个函
    数的⽬的在于可以实现两种状态下的处理
  • 1.6 所谓的condition就是整数,内部通过整数⽐较条件
  • 源码案例:
- (void)lg_testConditonLock{
    // 信号量
    NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        
        
//        [self.myLock mylockWithCondition:2];
         [conditionLock lockWhenCondition:1];
        NSLog(@"线程 1");
//        [self.myLock myUnlockWithCondition:0];
         [conditionLock unlockWithCondition:0];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
       
        [conditionLock lockWhenCondition:2];
//       [self.myLock mylockWithCondition:1];
     
       NSLog(@"线程 2");
//        [self.myLock myUnlockWithCondition:1];
//       self.myLock.value = 1;
        [conditionLock unlockWithCondition:1];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(1);
       [conditionLock lock];
       NSLog(@"线程 3");
       [conditionLock unlock];
    });
}
// 结果打印线程为:2,1,3

分析:

  • 首先都是全局异步线程,第二个线程设置Condition:2,先走lockWhenCondition:2的线程,这个线程的unlockWithCondition:1,然后会接着走第一个线程,因为第一个线程WhenCondition为1。所以必是先2 后 1
  • 第三个线程,可能在2,1之前,也可能在2,1之后,而3又sleep了1s,因此3很可能在2,1之后。

自旋锁

  • 当发现其他线程执行时,当前线程,会询问和忙等,耗费性能比较高,实用场景:短小的任务处理

互斥锁

  • 发现其他线程执行时,当前线程会休眠(就绪状态),等待唤醒执行
  • 互斥锁⼩结
    • 保证锁内的代码,同⼀时间,只有⼀条线程能够执⾏!
    • 互斥锁的锁定范围,应该尽量⼩,锁定范围越⼤,效率越差!
  • 互斥锁参数
    • 能够加锁的任意 NSObject 对象
    • 注意:锁对象⼀定要保证所有的线程都能够访问
    • 如果代码中只有⼀个地⽅需要加锁,⼤多都使⽤ self,这样可以避免单独再创建⼀个锁对象

读写锁

介绍

  • 读写锁实际是⼀种特殊的⾃旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进⾏读访问,写者则需要对共享资源进⾏写操作。
  • 这种锁相对于⾃旋锁⽽⾔,能提⾼并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最⼤可能的读者数为实际的逻辑CPU数。
    0, 写者是排他性的,⼀个读写锁同时只能有⼀个写者或多个读者(与CPU数相关),但不能同时。
    1,既有读者⼜有写者。在读写锁保持期间也是抢占失效的。
    2,如果读写锁当前没有读者,也没有写者,那么写者可以⽴刻获得读写锁,否则它必须⾃旋在那⾥,直到没有任何写者或读者。
    3,如果读写锁没有写者,那么读者可以⽴即获得该读写锁,否则读者必须⾃旋在那⾥,直到写者释放该读写锁。
    4,⼀次只有⼀个线程可以占有写模式的读写锁, 但是可以有多个线程同时占有读模式的读写锁.
    正是因为这个特性:
  • 当读写锁是写加锁状态时, 在这个锁被解锁之前, 所有试图对这个锁加锁的线程都会被阻塞.
  • 当读写锁在读加锁状态时, 所有试图以读模式对它进⾏加锁的线程都可以得到访问权, 但是如果线程希望以写模式对此锁进⾏加锁, 它必须直到所有的线程释放锁.
  • 通常, 当读写锁处于读模式锁住状态时, 如果有另外线程试图以写模式加锁, 读写锁通常会阻塞
  • 随后的读模式锁请求, 这样可以避免读模式锁⻓期占⽤, ⽽等待的写模式锁请求⻓期阻塞.
  • 读写锁适合于对数据结构的读次数⽐写次数多得多的情况. 因为, 读模式锁定时可以共享, 以写模式锁住时意味着独占, 所以读写锁⼜叫共享-独占锁.

原理

核心点:多读单写读写互斥,写入时不能堵塞任务执行(使用栅栏函数实现写入, 并发队列实现多读

  • 读写锁适合于对数据结构的读次数⽐写次数多得多的情况. 因为, 读模式锁定时可以共享, 以写模式锁住时意味
    着独占, 所以读写锁⼜叫共享-独占锁.
    #include <pthread.h>
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
    成功则返回0, 出错则返回错误编号.
    同互斥量以上, 在释放读写锁占⽤的内存之前, 需要先通过pthread_rwlock_destroy对读写锁进⾏清理⼯作, 释
    放由init分配的资源.
    #include <pthread.h>
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    成功则返回0, 出错则返回错误编号.
    这3个函数分别实现获取读锁, 获取写锁和释放锁的操作. 获取锁的两个函数是阻塞操作, 同样, ⾮阻塞的函数为:
    #include <pthread.h>
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    成功则返回0, 出错则返回错误编号.
    ⾮阻塞的获取锁操作, 如果可以获取则返回0, 否则返回错误的EBUSY.

线程和Runloop的关系

  • 1:runloop与线程是⼀⼀对应的,⼀个runloop对应⼀个核⼼的线程,为什么说是核⼼的,是因为runloop是可以嵌套的,但是核⼼的只能有⼀个,他们的关系保存在⼀个全局的字典⾥。
  • 2:runloop是来管理线程的,当线程的runloop被开启后,线程会在执⾏完任务后进⼊休眠状态,有了任务就会被唤醒去执⾏任务。
  • 3:runloop在第⼀次获取时被创建,在线程结束时被销毁。
  • 4:对于主线程来说,runloop在程序⼀启动就默认创建好了。
  • 5:对于⼦线程来说,runloop是懒加载的,只有当我们使⽤的时候才会创建,所以在⼦线程⽤定时器要注意:确保⼦线程的runloop被创建,不然定时器不会回调。

Runloop

认识

Runloop一直在控制着app程序的运行,使线程保活。一直do…while循环,但又跟循环不一样。
它的作用:
• 保持程序的持续运行
• 处理APP中的各种事件(触摸、定时器、performSelector)
• 节省cpu资源、提供程序的性能:该做事就做事,该休息就休息
在这里插入图片描述

runloop探索

底层封装的是CFRunLoopRun,为do…while循环。
在这里插入图片描述
在这里插入图片描述
_CFRunLoopGet0 - > _CFRunLoopCreat()
在这里插入图片描述

__CFRunLoop为结构体类型。

请添加图片描述

在这里插入图片描述

  • 线程通过 CFMutableDictionaryRef绑定runloop
  • 每个线程都有个runloop,而runloop可以有多个mode,mode里面可以有多个source,多个timer,多个observer

统称item
在这里插入图片描述
• block应用:CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
• 调用timer:CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
• 响应source0: CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
• 响应source1: CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
• GCD主队列:CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
• observer源: CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION

runloop原理

在这里插入图片描述

Runloop的几种状态
  • 六种状态:
    RunLoopEntry
    RunLoopBeforeTimers
    RunLoopBeforeWaiting
    RunLoopAfterWaiting
    RunLoopExit
    RunLoopAllActivities
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

☆MOON

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值