前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

iOS-GCD

原创
作者头像
Wilbur-L
修改2021-02-18 09:28:21
5640
修改2021-02-18 09:28:21
举报
文章被收录于专栏:iOS底层原理iOS底层原理

sync同步函数serial串行队列:不会开启线程,在当前线程执行任务,会产生堵塞

sync同步函数Concurrent并发队列:不会开启线程,任务一个接着一个,不会产生堵塞

async异步函数serial串行队列:开启线程一条新线程,任务一个接着一个

async异步函数Concurrent并发队列:开启线程,在当前线程执行任务,任务异步执行,没有顺序与cpu调度有关

同步:等待+1 串行:等待+1

主队列:专门用来在线程上调度任务的串行队列,不会开启线程,如果当前主线程有任务正在执行那么主队列的任务都不会被调度

全局队列:一个并发队列,且不能被栅栏函数作用,在使用多线程开发时,如果没有特殊需求,执行异步任务,默认使用全局队列

注释:DISPATH_QUEUE_WIDTH_FULL 0x1000ull

dispath_async(queue_t dq,block_t work){

1.申请空间 continuation

2.初始化init

dispath_continuation_init(dc,dq,work,0,dc_flags){

dc:申请的空间

dq:当前队列

work:任务

flags:标识

dispatch_function_t func = dispatch_Block_invoke(work)->dispatch_call_block_and_release(ctxt)

dc->dc_ctxt = ctxt (dc内的属性是alloc出来的)

dc->dc_func = f

qos= init_f(dc,dqu,ctxt,func,flags,dc_flags)

dispatch_continuation_async(dq,dc,qos,dc->dc_flags)

pop_inline-(dispatch_object_t dou )

}

GCD-单例

单例dispatch_once(dispatch_once_t *val ,ctxt,func){

dispatch_once_gate_t l=val;

1.v = os_atomic_load(&l->dgo_once,acquire)

2.if v==DLOCK_ONCE_DONE ->return

tryenter(l){加锁

3.os_atomic_cmpxchg(&l->dog_once,DLOCK_ONCE_UNLOCKED,lock_value_for_self,relaxed)

4.dispatch_once_callout(l,ctxt,func){gate_broadcast(l) 对已经调用的单例l进行广播}

}

5.broadcast(dispatch_once_gate_t l){

6.dispatch_lock value_self = _dispatch_lock_value_for_self();上一步比较cmp操作用到的参数,作用是修改v值,使下次再无法再次调用单例

7.v = dispatch_once_mark_done(l) //单例作用已完成 标记mark .v返回给os_atomic_load

{

8.return os_atomic_xchg(&dgo->dgo_once,DLOCK_ONCE_DONE,release)这个就是V返回给2

}

}

return once_wait(l)

}

GCD-栅栏函数(barrier)

dispatch_barrier_async(异步) 阻塞队列

dispatch_barrier_sync(同步) 阻塞线程

注释:栅栏函数只能传入一个自己创建的并发队列

GCD-同步函数

dispatch_sync_f_inline(dq,ctxt,func,dc_flags)

dq->dq_width==1 return _dispatch_barrier_sync_f(dq,ctxt,func,dc_flags)

dispatch_sync_function_invoke_inlie(dq,ctxt,func)

thread_frame dtf

push(&dtf,dq)

client_callout(ctxt,func) //block函数

pop(&dtf)

return _dispatch_lane_barrier_complete(dq)//释放

os_atomic_rww_loop_give_up 护犊子操作

GCD-死锁

dispatch_sync_f_slow

lock_by(lock_value,tid)

return (lock_value ^ tid)&DLOCK_OWNER_MASK == 0 //异或操作 相同=0 不同=1

判断当前队列与正在执行的队列是否相同 返回一个状态值

dq_state_drain_lock_by(dq_state,dispatch_sync_context_t dsc->dsc_waiter)

DISPATCH_CLIENT_CRASH(dq_state){

"dispatch_sync called on queue already owned by current thread"

}

GCD-调度组

搭配使用(实际上就是同步效果)

dispatch_group_async 进组任务

dispatch_group_create{

dg=alloc(group,sizeof(groups))

dg->do_next=LISTLESS

dg->do_target=default_queue

return dg

}

dispatch_group_enter{

old_bits=os_atomic_sub_orig2o(dg,dg_bits,GROUP_VALUE,acquire)

old_value=old_bits&MASK

如果old_value==0 retain(dg)

如果old_value==VALUE_MAX(CRASH"Too many nested calls to dispatch_group_enter")

}

dispatch_group_notify(监听调度组)只保证enter 和 leave 成对存在(dispatch_group_t dg,dq,dsn){

如果old_state==0 os_atomic_rmw_loop_give_up &return dispatch_group_weak苏醒

异步函数push pop invoke 等等

}

dispatch_group_leave差不多的操作{

new_state,old_state = os_atomic_add_orig2o(dg,dg_state,VALUE,release)

os_atomic_rmw_loop_give_up &return dispatch_group_weak苏醒

if old_value==0 ("Unbalanced call to dispatch leave")

}

GCD-信号量

dispatch_semphore_create(n) 控制GCD最大并发数

n=1 同步

n>=2 异步

dispatch_semphore_signal(dispatch_semaphore_t dsema){

os_atomic_inc2o(dsema,dsema_value,release)

add(dsema->dsema_value,1,m,add)

}

if value>0 return

or return signal_slow(dsema)长等待

总而言之:

1.create创建信号量

2.wait等待 os_atomic_des2o(dsema,dsema_value,release){

des(dsema->dsema_value,1,m,des)

if value >=0 return

}

3.释放

dispatch_semaphore_wait_slow(dsema,timeout)

GCD-source

create 创建源

set_event_handler 设置事件回调

merge_data 事件数据

get_data

resume 继续

suspend 挂起

GCD-synchronized

synchronized{

1.objc_sync_enter

2.objc_sync_exit

}

Lv1 源码层:

enter{

SyncData *data=id2data(obj,Acquired)

data->mutex.lock()

}

exit{

OBJC_Sync_success

Bool ok=data->mutex.unlock()

}

Lv2 :

typedef struct alignas(CacheLineSize)SyncData{

struct SyncData* nextData;下一个节点 链表结构

recursive_mutex_t mutex 定义一个递归锁

threadCount //计算有多少个线程正在使用这个block

}

id2Data{

tls->kvc 暂存空间,检查每个线程的单入口类匹配当前对象

bool fastcacheOccupied=No;

SyncData *data=tls_get_direct 拿到Key

if (data){

fastcacheOccupied=Yes

又一岑判断当前对象是否为key所绑定的对象

if data->objcet==object{

lockCount 记录有多少把锁,锁住了这个对象

lockCount=tls_get_direct(SYNC_COUNT_KEY)

switch(Acquired,Release)

tls_set_direct(COUNT_KEY,lockCount)

result->nextData =*listp 如果当前对象数据有下层依赖,切换成下一个节点

*listp = result

lockp.lock

{

SyncData *p

SyncData firstUnused = NULL;

for (p=*listp;p!=NULL;p=p->nextData){

且判断是否有下一个节点,如果没有break,还有继续循环

循环访问p->nextData 访问尾节点

result = p 结果赋值

OSAutomicIncrement32Barrier(&result->threadCount)

调用OS底层(取到线程数量地址)

}

Release check 没有正确的数据关联到这个对象

if firstUnused != NULL{

如果该对象并不是第一次上锁

那么该对象有三种状态

1.第一次没有锁

2.不是第一次,同一个线程加的锁

3.不是第一次,不同进程加的锁

存到tls临时变量里方便缓存cache查找,如果有多个线程锁同一个对象,必定在cache留下痕迹

}

if (!fastcacheOccupied){

tls_set_direct{DATA_KEY,COUNT_KEY}

}

if (!cache){

fetch_cache(YES)

cache->list[cache->used].data=result

cache->list[cache->used].lockCount = 1

cache->used ++

if(cache){

for i=0;i<cache->used;i++{

SyncCacheItem *item = &cache->list[i]

if (item->data->object!=objcet)继续查找{

result=item->data 寻找下一个节点数据

直到在cache找到,证明不是第一次加锁 如果cache判断为真

找到有两种操作 Required / Releas

break

}

}

}

}

}

}

}

}id2Data结束

总结:Synchronized为何性能如此低,且泛用性高,不用忘记解锁

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • GCD-单例
  • GCD-栅栏函数(barrier)
  • GCD-同步函数
  • GCD-死锁
  • GCD-调度组
  • GCD-信号量
  • GCD-source
  • GCD-synchronized
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档