xml地图|网站地图|网站标签 [设为首页] [加入收藏]

深入理解GCD之dispatch_queue,GCD的使用和原理

来源:http://www.ccidsi.com 作者:呼叫中心培训课程 人气:152 发布时间:2020-04-21
摘要:初藳链接深刻掌握GCD之dispatch_queue 参考: GCD源码 深切领悟 GCD iOS多线程--深透学会多线程之『GCD』 关于iOS二十四线程,我说,你听,没准你就懂了 在我们做iOS开采的经过中,平日会与

初藳链接深刻掌握GCD之dispatch_queue

参考:
GCD源码
深切领悟 GCD
iOS多线程--深透学会多线程之『GCD』
关于iOS二十四线程,我说,你听,没准你就懂了

在我们做iOS开采的经过中,平日会与多线程打交道,异步绘制,网络伏乞等,形式有NSThread,NSOperationQueue,GCD等,在那处GCD的身价主要,那么后天写一篇有关GCD的篇章。首先迎来第三个难题:

上一篇大家介绍了GCD的布局体,这一篇大家注重看一下GCD中队列的结合。队列是大家在应用GCD中常常接触的能力点。

职务履市场价格势

  • 一起推行(dispatch_sync):只可以在当下线程中施行职务,不辜负有开启新线程的本领。必须等到Block函数实施完成后,dispatch函数才会回来。
  • 异步实施(dispatch_async):能够在新的线程中实行职分,具有开启新线程的力量。dispatch函数会应声赶回, 然后Block在后台异步推行。

什么是GCD全名为 Grand Central Dispatch 是一种异步推行职责的手艺,一套基于c语言完结的api,语法十一分轻易,只须要简单定义职责或按需投入到行列中,就足以遵从布置达成效果与利益,下边二个大致的事例。

主队列和主线程

那八个术语大家能够不常听到,不亮堂有未有人会把那三个概念等同化。主队列和主线程是有关联,不过它们是四个分歧的定义。轻巧地说,主队列是主线程上的一个串行队列,是系统自动为大家创设的。换言之,主线程是足以实行除主队列之外其他队列的职责。

任务管理方法

  • 串行队列:全部职务会在一条线程中实践(有极大概率是当前线程也可能有十分大大概是新开荒的线程),并且多个任务执行达成后,才最西子行下二个任务。(等待完毕)
  • 人机联作队列:能够敞开多条线程并行实践任务(但不自然会敞开新的线程),并且当三个任务放到钦定线程伊始施行时,下贰个职责就可以开端实施了。(等待产生)
    // 主队列--串行,所有放在主队列中的任务,都会放到主线程中执行
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    // 全局队列--并行,系统为我们创建好的一个并行队列,使用起来与我们自己创建的并行队列无本质差别
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);

    // new串行队列
    dispatch_queue_t queue1 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

    // new并行队列
    dispatch_queue_t queue2 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

注意:防止选用 GCD Global队列创立Runloop常驻线程
大局队列的最底层是叁个线程池,向全局队列中付出的 block,都会被放到那么些线程池中实行,要是线程池已满,后续再交付 block 就不会再重复创制线程。等待有空暇的线程在奉行职务。
所以:
制止选拔 GCD Global 队列创建 Runloop 常驻线程,假如n条线程都被私吞了,Global队列就费了。

dispatch_async(dispatch_get_global_queue, ^{ //网络请求 //异步绘制图像 //数据库访问等 dispatch_async(dispatch_get_main_queue(), ^{ //刷新UI }); });

队列和线程

Concurrent Programming: APIs and Challenges中的一张图纸能够很直观地汇报GCD与线程之间的涉及:

图片 1

二个线程内恐怕有八个种类,那几个队列恐怕是串行的可能是并行的,依照联合依然异步的艺术行事。

dispatch_queue_s是队列的构造体,能够说咱俩在GCD中接触最多的布局体了。

struct dispatch_queue_vtable_s { DISPATCH_VTABLE_HEADER(dispatch_queue_s);};#define DISPATCH_QUEUE_MIN_LABEL_SIZE 64#ifdef __LP64__#define DISPATCH_QUEUE_CACHELINE_PAD 32#else#define DISPATCH_QUEUE_CACHELINE_PAD 8#endif#define DISPATCH_QUEUE_HEADER  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;struct dispatch_queue_s { DISPATCH_STRUCT_HEADER(dispatch_queue_s, dispatch_queue_vtable_s); DISPATCH_QUEUE_HEADER; char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]; // must be last char _dq_pad[DISPATCH_QUEUE_CACHELINE_PAD]; // for static queues only};

GCD中选取了众多的宏,不便于大家知晓代码,大家用相应的构造替换掉定义的宏,如下:

struct dispatch_queue_s { //第一部分:DISPATCH_STRUCT_HEADER(dispatch_queue_s, dispatch_queue_vtable_s) const struct dispatch_queue_vtable_s *do_vtable;  //dispatch_queue_s的操作函数:dispatch_queue_vtable_s类型的结构体 struct dispatch_queue_s *volatile do_next;  //链表的next unsigned int do_ref_cnt;  //引用计数 unsigned int do_xref_cnt;  //外部引用计数 unsigned int do_suspend_cnt;  //暂停标志,比如延时处理中,在任务到时后,计时器处理将会将该标志位修改,然后唤醒队列调度 struct dispatch_queue_s *do_targetq;  //目标队列,GCD允许我们将一个队列放在另一个队列里执行任务 void *do_ctxt;  //上下文,用来存储线程池相关数据,比如用于线程挂起和唤醒的信号量、线程池尺寸等 void *do_finalizer; //第二部分:DISPATCH_QUEUE_HEADER uint32_t volatile dq_running;  //是否运行中 uint32_t dq_width;  //最大并发数:主线程/串行中这个值为1 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; //specific队列 //其他: char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]; // must be last 说明队列的名字要少于64个字符 char _dq_pad[DISPATCH_QUEUE_CACHELINE_PAD]; // for static queues only};

队列的类型能够分为主队列治本种类自定义队列大局队列4种类型。

任务 队列

串行队列 并行队列 主队列
同步(sync) 当前线程,串行执行 队列当前线程,串行执行 主新线程,串行执行(注意死锁)
异步(async) 开1条新线程,串行执行 开n条新线程,异步执行(n在iphone7上面最大是几十个) 主新线程,串行执行

在此边运用异步的法子定义了二个职务,并将其增加到叁个大局队列中,这其间的职责内容可以扩充部分耗费时间操作,由于是异步所以不影响主线程,当职务完成之后,相同接纳异步的措施定义了叁个任务,将其增多到了主队列中,这里实行的任务会在主线程完毕。

主队列

我们在付出进度中得以应用dispatch_get_main_queue获取主队列,看一下它的概念:

#define dispatch_get_main_queue() (&_dispatch_main_q)struct dispatch_queue_s _dispatch_main_q = {#if !DISPATCH_USE_RESOLVERS .do_vtable = &_dispatch_queue_vtable, .do_targetq = &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY],#endif .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .dq_label = "com.apple.main-thread", .dq_running = 1, .dq_width = 1, //说明主队列是一个串行队列 .dq_serialnum = 1,};

它的多少个根特品质:

1.do_vtable

const struct dispatch_queue_vtable_s _dispatch_queue_vtable = { .do_type = DISPATCH_QUEUE_TYPE, .do_kind = "queue", .do_dispose = _dispatch_queue_dispose, .do_invoke = NULL, .do_probe = dummy_function_r0, .do_debug = dispatch_queue_debug,};

2.do_targetq

主队列的靶子队列:"com.apple.root.default-overcommit-priority"那些全局队列。这里大家先提前线总指挥部结一下:非全局队列的种类类型(主队列甚至背后提到的军管类别和自定义队列),都亟待压入到全局队列管理,所以须要安装do_targetq

[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.default-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 7, },

3.do_ref_cnt和do_xref_cnt

最近提到do_ref_cntdo_xref_cnt是引用计数,主队列的那八个值为DISPATCH_OBJECT_GLOBAL_REFCNT。既然是援引计数,那也许是和GCD的内部存储器管理有关,找到和内部存储器管理有关的代码:

voiddispatch_retain(dispatch_object_t dou){ if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; // global object } ...}void_dispatch_retain(dispatch_object_t dou){ if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; // global object } ...}voiddispatch_release(dispatch_object_t dou){ if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; } ...}void_dispatch_release(dispatch_object_t dou){ if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { return; // global object } ...}

从上面多少个函数大家得以看出,主队列的生命周期是伴随着应用的,不会受retain和release的影响

Dispatch Block

队列施行职务都是block的法子,

一同和异步日前内容提到了“异步”一词,那么这里聊一下哪些是“异步”,相应的还应该有“同步”一词。异步的效率是不须求CPU顿时响应,而是等待二个频域信号或回调来下一步操作,当前线程可以至时推行后续内容,而一起为能够卡住当前线程,当试行完三个职分之后手艺履行下一个。异步是目的,多线程是手段当大家张开三个新的线程之后,能够将职务交由新的线程实施,达成异步效果,当须求的时候再回到以前的线程。

管住种类

_dispatch_mgr_q,是GCD的中间队列,不对曾祖父开。从名字上看,那一个队列应该是用来饰演管理的剧中人物,GCD停车计时器就用到了保管类别。

struct dispatch_queue_s _dispatch_mgr_q = { .do_vtable = &_dispatch_queue_mgr_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_targetq = &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.libdispatch-manager", .dq_width = 1, .dq_serialnum = 2,};

1.do_vtable

static const struct dispatch_queue_vtable_s _dispatch_queue_mgr_vtable = { .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,};

2.do_targetq

管理连串的靶子队列:"com.apple.root.high-overcommit-priority"那么些大局队列。

[DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.high-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 9, },

3.do_ref_cnt和do_xref_cnt

管理类别的那多少个值为DISPATCH_OBJECT_GLOBAL_REFCNT,所以和主队列的生命周期应该是同样的。

创建block
- (void)createDispatchBlock {
    // 一般的block
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"run block");
    });
    dispatch_async(concurrentQueue, block);

    //QOS优先级的block
    dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
        NSLog(@"run qos block");
    });
    dispatch_async(concurrentQueue, qosBlock);
}

dispatch_block_wait:能够依赖dispatch block来设置等待时间,参数DISPATCH_TIME_FOREVE奥德赛会平素等候block甘休

- (void)dispatchBlockWaitDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"star");
        [NSThread sleepForTimeInterval:5.f];
        NSLog(@"end");
    });
    dispatch_async(serialQueue, block);
    //设置DISPATCH_TIME_FOREVER会一直等到前面任务都完成
    dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
    NSLog(@"ok, now can go on");
}

dispatch_block_notify:能够监视钦命dispatch block甘休,然后再投入三个block到行列中。两个参数分别为,第一个是内需监视的block,第一个参数是须求提交试行的队列,第多少个是待参加到行列中的block

- (void)dispatchBlockNotifyDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"first block start");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"first block end");
    });
    dispatch_async(serialQueue, firstBlock);
    dispatch_block_t secondBlock = dispatch_block_create(0, ^{
        NSLog(@"second block run");
    });
    //first block执行完才在serial queue中执行second block
    dispatch_block_notify(firstBlock, serialQueue, secondBlock);
}

dispatch_block_cancel:iOS8过后方可调用dispatch_block_cancel来撤消(需求小心必需用dispatch_block_create创建dispatch_block_t)
急需当心的是,未举办的能够用此方式cancel掉,若已经实践则cancel不了
就算想中断(interrupt)线程,能够采用dispatch_block_testcancel方法

- (void)dispatchBlockCancelDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"first block start");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"first block end");
    });
    dispatch_block_t secondBlock = dispatch_block_create(0, ^{
        NSLog(@"second block run");
    });
    dispatch_async(serialQueue, firstBlock);
    dispatch_async(serialQueue, secondBlock);
    //取消secondBlock
    dispatch_block_cancel(secondBlock);
}

用图来做四个联合和异步的演讲

自定义队列

咱俩在付出中会使用dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)始建二个自定义的队列。它的源代码如下:

dispatch_queue_tdispatch_queue_create(const char *label, dispatch_queue_attr_t attr){ dispatch_queue_t dq; size_t label_len; if  { label = ""; } label_len = strlen; if (label_len < (DISPATCH_QUEUE_MIN_LABEL_SIZE - 1)) { label_len = (DISPATCH_QUEUE_MIN_LABEL_SIZE - 1); } // XXX switch to malloc() dq = calloc(1ul, sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_MIN_LABEL_SIZE - DISPATCH_QUEUE_CACHELINE_PAD   label_len   1); if (slowpath { return dq; }//队列初始化数据 _dispatch_queue_init; strcpy(dq->dq_label, label); if (fastpath { return dq; } if (fastpath(attr == DISPATCH_QUEUE_CONCURRENT)) { dq->dq_width = UINT32_MAX; dq->do_targetq = _dispatch_get_root_queue; } else { dispatch_debug_assert(!attr, "Invalid attribute"); } return dq;}

1.slowpathfastpath

有关那七个宏的概念如下:

#define fastpath ()__builtin_expect, ~0l))#define slowpath ()__builtin_expect, 0l))

fastpath代表x的值日常不为0,希望编写翻译器进行优化。slowpath意味着x的值很也许为0,希望编写翻译器进行优化。

2._dispatch_queue_init

static inline void_dispatch_queue_init(dispatch_queue_t dq){ dq->do_vtable = &_dispatch_queue_vtable; dq->do_next = DISPATCH_OBJECT_LISTLESS; dq->do_ref_cnt = 1; dq->do_xref_cnt = 1; // Default target queue is overcommit! dq->do_targetq = _dispatch_get_root_queue; dq->dq_running = 0; dq->dq_width = 1; dq->dq_serialnum = dispatch_atomic_inc(&_dispatch_queue_serial_numbers) - 1;}

_dispatch_queue_init暗中认可设置二个行列为串行队列,它的目的队列是_dispatch_get_root_queue

3.do_targetq

前面对这么些字段的演说多少轻松了,do_targetq代表目的队列。在Concurrent Programming: APIs and Challenges提到:

While custom queues are a powerful abstraction, all blocks you schedule on them will ultimately trickle down to one of the system’s global queues and its thread pool.

就算如此自定义队列是多少个无敌的虚幻,但您在队列上安排的保有Block最后都会渗透到系统的某三个大局队列及其线程池。

看起来自定义队列更像是全局队列的一个代理。在自定义队列成立的时候私下认可其指标队列为_dispatch_get_root_queue。其中0意味着优先级DISPATCH_QUEUE_PRIORITY_DEFAULTtrue代表是不是是overcommitovercommit参数表示该队列在执行block时,不论系统多忙都会新开七个线程,那样做的指标是不会形成有个别线程过载。要是是自定义并发队列的话,do_targetq会被设置为_dispatch_get_root_queue

值得注意的是,主队列的对象队列也是一个大局队列,全局队列的后面部分正是平时的线程池(那个会在大局队列中讲到)。

4.dq_serialnum

dq_serialnum是在_dispatch_queue_serial_numbers基础上开展原子操作加1,即从12开班加多。1到11被保留的类别号定义如下:

// skip zero// 1 - main_q// 2 - mgr_q// 3 - _unused_// 4,5,6,7,8,9,10,11 - global queues// we use 'xadd' on Intel, so the initial value == next assigned

中间1用于主队列,2用于管理类别,3临风尚未被运用,4~11是用来全局队列的。由于本身找错了看的源码是libdispatch-187.10很老了,前边苹果有新扩展了多少个连串。

1. 串行队列 同步实施

不会敞开新线程,在当下线程实施任务。任务是串行的,实行完二个职分,再进行下叁个职责

- (void)serialQueueSync{

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t serialQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);

    /*
     NSLog输出
     0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     */

}

图片 2异步和协助进行

大局队列

上边说了重重大局队列,现在我们来看一下大局队列是怎么样定义的。

dispatch_queue_tdispatch_get_global_queue(long priority, unsigned long flags){ if (flags & ~DISPATCH_QUEUE_OVERCOMMIT) { return NULL; } return _dispatch_get_root_queue(priority, flags & DISPATCH_QUEUE_OVERCOMMIT);}static inline dispatch_queue_t_dispatch_get_root_queue(long priority, bool overcommit){ if (overcommit) switch  { case DISPATCH_QUEUE_PRIORITY_LOW: return &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY]; case DISPATCH_QUEUE_PRIORITY_DEFAULT: return &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY]; case DISPATCH_QUEUE_PRIORITY_HIGH: return &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY]; case DISPATCH_QUEUE_PRIORITY_BACKGROUND: return &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY]; } switch  { case DISPATCH_QUEUE_PRIORITY_LOW: return &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY]; case DISPATCH_QUEUE_PRIORITY_DEFAULT: return &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY]; case DISPATCH_QUEUE_PRIORITY_HIGH: return &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY]; case DISPATCH_QUEUE_PRIORITY_BACKGROUND: return &_dispatch_root_queues[ DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY]; default: return NULL; }}DISPATCH_CACHELINE_ALIGNstruct dispatch_queue_s _dispatch_root_queues[] = { [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY], .dq_label = "com.apple.root.low-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 4, }, [DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.low-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 5, }, [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY], .dq_label = "com.apple.root.default-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 6, }, [DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.default-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 7, }, [DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY], .dq_label = "com.apple.root.high-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 8, }, [DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.high-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 9, }, [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY], .dq_label = "com.apple.root.background-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 10, }, [DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY] = { .do_vtable = &_dispatch_queue_root_vtable, .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, .do_ctxt = &_dispatch_root_queue_contexts[ DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY], .dq_label = "com.apple.root.background-overcommit-priority", .dq_running = 2, .dq_width = UINT32_MAX, .dq_serialnum = 11, },};

1.do_vtable

大局队列的do_vtable

static const struct dispatch_queue_vtable_s _dispatch_queue_root_vtable = { .do_type = DISPATCH_QUEUE_GLOBAL_TYPE, .do_kind = "global-queue", .do_debug = dispatch_queue_debug, .do_probe = _dispatch_queue_wakeup_global,};

2.do_ctxt

全局队列中有三个上下文的天性,用来存款和储蓄线程池相关数据,比方用来线程挂起和提醒的非复信号量、线程池尺寸等。

它的定义如下:

static struct dispatch_root_queue_context_s _dispatch_root_queue_contexts[] = { [DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY] = {#if DISPATCH_ENABLE_THREAD_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_ENABLE_THREAD_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_ENABLE_THREAD_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_ENABLE_THREAD_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_ENABLE_THREAD_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_ENABLE_THREAD_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_ENABLE_THREAD_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_ENABLE_THREAD_POOL .dgq_thread_mediator = &_dispatch_thread_mediator[ DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY], .dgq_thread_pool_size = MAX_THREAD_COUNT,#endif },};
2. 串行队列 异步试行

开叁个新线程,叁个三个实施职分

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);

    /*
     NSLog输出
     0========<NSThread: 0x6000002616c0>{number = 1, name = main}
     4========<NSThread: 0x6000002616c0>{number = 1, name = main}
     1========<NSThread: 0x608000270540>{number = 3, name = (null)}
     2========<NSThread: 0x608000270540>{number = 3, name = (null)}
     3========<NSThread: 0x608000270540>{number = 3, name = (null)}
     */

此间thread1模拟主线程,当实践职分A的时候由于耗费时间非常的短,所以能够在主线程上完成,当推行到任务B的时候,由于耗费时间间长度,所以开采了一条新的线程,将职务交由子线程管理,同不经常间继续实现职分C,当耗费时间操作实现之后,将结果重回给主线程实行操作,达成了异步效能。

dispatch_sync

dispatch_sync的源码如下:

voiddispatch_sync(dispatch_queue_t dq, void {#if DISPATCH_COCOA_COMPAT if (slowpath(dq == &_dispatch_main_q)) { return _dispatch_sync_slow; }#endif struct Block_basic *bb = work; dispatch_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke);}

一旦那么些行列是主队列,则调用_dispatch_sync_slow,不然调用dispatch_sync_f。点开_dispatch_sync_slow返现,最终照旧调用了dispatch_sync_f方法。通过_dispatch_Block_copy或者Block_basic完成由block到function的转换。进而block的执行底层依然使用function

3. 并行队列 同步推行

日前线程,三个二个推行职责

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);

    /*
     NSLog输出
     0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     */

行使四线程照旧要在乎多少个难点,1.数码角逐 2.死锁 3.线程开发太多招致内部存储器消耗过大,可是纵然利用合适,多线程对我们开采的裨益十二分伟大。

dispatch_sync_f

dispatch_sync_f的源码如下:

voiddispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func){ //串行队列 if (fastpath(dq->dq_width == 1)) { return dispatch_barrier_sync_f(dq, ctxt, func); } //全局队列 if (slowpath(!dq->do_targetq)) { // the global root queues do not need strict ordering dispatch_atomic_add2o(dq, dq_running, 2); return _dispatch_sync_f_invoke(dq, ctxt, func); } //并发队列 _dispatch_sync_f2(dq, ctxt, func);}

这里分成了三种景况:

  1. 要是是串行队列,施行dispatch_barrier_sync_f
  2. 一旦是大局队列,实行_dispatch_sync_f_invoke
  3. 若果是并行队列,实践_dispatch_sync_f2

假使若串行队列压入同步任务,那么当前职分就一定要等待前方的天职施行到位后技巧试行。源代码就能调用dispatch_barrier_sync_f函数完结地方的效能。

voiddispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func){ // 1) ensure that this thread hasn't enqueued anything ahead of this call // 2) the queue is not suspended //第一步:如果串行队列中存在其他任务或者队列被挂起,则直接进入_dispatch_sync_f_slow函数,等待这个队列中的其他任务完成,然后执行这个任务。 if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED{ return _dispatch_barrier_sync_f_slow(dq, ctxt, func); } //第二步:检测队列的dq_running状态,如果有运行,进入_dispatch_barrier_sync_f_slow,等待激活。 if (slowpath(!dispatch_atomic_cmpxchg2o(dq, dq_running, 0, 1))) { // global queues and main queue bound to main thread always falls into // the slow case return _dispatch_barrier_sync_f_slow(dq, ctxt, func); } //第三步:有多重队列,寻找真正的目标队列,其实还是回到了dispatch_sync_f方法 if (slowpath(dq->do_targetq->do_targetq)) { return _dispatch_barrier_sync_f_recurse(dq, ctxt, func); } //第四步:执行队列里的任务,执行后检测队列有无其他任务,如果有,释放前面的信号量(释放信号_dispatch_barrier_sync_f2函数中)。 _dispatch_barrier_sync_f_invoke(dq, ctxt, func);}

看了上边的代码注释后,大家来想转手协助实行串行队列死锁难点。死锁是怎么发生的?先看下示例代码:

#import "DeadLock.h"@implementation DeadLock- (instancetype)init { if (self = [super init]) {// [self _mianQueueDeadLock]; [self _serialQueueDeadLock]; } return self;}#pragma mark - Private- _mianQueueDeadLock { dispatch_sync(dispatch_get_main_queue(), ^{ NSLog; });}- _serialQueueDeadLock { dispatch_queue_t queue1 = dispatch_queue_create("1serialQueue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue2 = dispatch_queue_create("2serialQueue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue1, ^{ NSLog; dispatch_sync(queue1, ^{//如果使用queue2就不会发生死锁,使用queue1就会死锁 NSLog; }); });}@end

_serialQueueDeadLock为例:当第三回实行串行队列职分的时候,跳到第四步,直接最初进行职分,在运营第2个dispatch_sync时候,在职分之中通过推行第一步入那几个合伙队列中压入非能量信号量,然后等待功率信号量,步向死锁。假使主队列则会跳转到第二步步向死锁。

static void_dispatch_sync_f_invoke(dispatch_queue_t dq, void *ctxt, dispatch_function_t func){ _dispatch_function_invoke(dq, ctxt, func); if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) { _dispatch_wakeup; }}

假如当前队列是全局队列的话,就能调用_dispatch_sync_f_invoke。那个函数的作用:实践传入的任务,然后依照dq_running检验职责队列有未有激活,没有激活就实践激活函数。关于激活函数_dispatch_wakeup放在队列的异步中等教育授。

_dispatch_sync_f2(dispatch_queue_t dq, void *ctxt, dispatch_function_t func){ // 1) ensure that this thread hasn't enqueued anything ahead of this call // 2) the queue is not suspended //第一步:并发队列中有其他任务或者队列被挂起,压入信号量,等待其他线程释放这个信号量 if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED{ return _dispatch_sync_f_slow(dq, ctxt, func); } //第二步:并行队列没激活,激活队列后执行任务,最终还是调用了_dispatch_sync_f_slow函数,只是多了一个激活函数 if (slowpath(dispatch_atomic_add2o(dq, dq_running, 2) & 1)) { return _dispatch_sync_f_slow2(dq, ctxt, func); } //第三步:队列有多重队列,寻找真正的目标队列 if (slowpath(dq->do_targetq->do_targetq)) { return _dispatch_sync_f_recurse(dq, ctxt, func); } //第四步:并行队列没有其他任务,调用并激活这个队列 _dispatch_sync_f_invoke(dq, ctxt, func);}

经过地方的讲授,并行队列同步履行是逐个实践的。这种顺序实施和操作队列为并发队列未有涉及。而是因为那几个操作均为同步操作,所以种种操作放入队列后都会被等候实施到位才会归入下一操作,产生了这种顺序实践的情景。

今昔我们收拾一下行列同步施行的流程,如下图:图片 3dispatch_sync

说罢了一齐我们今日看一下异步。我们接纳dispatch_async进展队列的异步履行。

4. 并行队列 异步施行

开四个线程,异步推行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);

    /*
     NSLog输出
     0========<NSThread: 0x608000070300>{number = 1, name = main}
     4========<NSThread: 0x608000070300>{number = 1, name = main}
     2========<NSThread: 0x608000264140>{number = 4, name = (null)}
     1========<NSThread: 0x60000007a800>{number = 3, name = (null)}
     3========<NSThread: 0x6080002642c0>{number = 5, name = (null)}
     */

串行队列和现身队列首先解释一下“队列”,如其名字,它是实践拍卖的等候队列,这里的队列调整方式为先进先出格局(FIFO-First In First Out卡塔尔(قطر‎,也等于先增添到队列中的职责先进行,当然还应该有别的两种形式,不过这里这几天不考虑,风野趣的请自行查资料

dispatch_async

dispatch_async的源码如下:

voiddispatch_async(dispatch_queue_t dq, void { dispatch_async_f(dq, _dispatch_Block_copy, _dispatch_call_block_and_release);}

dispatch_async第一将block从栈copy到堆上,也许扩张引用计数,保证block在奉行早前不会被消亡,别的_dispatch_call_block_and_release用于销毁block。然后调用dispatch_async_f

5. 主队列 异步实行

主线程,同步实行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);

    /*
     NSLog输出
     0========<NSThread: 0x60000026e000>{number = 3, name = (null)}
     4========<NSThread: 0x60000026e000>{number = 3, name = (null)}
     1========<NSThread: 0x60000007e2c0>{number = 1, name = main}
     2========<NSThread: 0x60000007e2c0>{number = 1, name = main}
     3========<NSThread: 0x60000007e2c0>{number = 1, name = main}
     */

图片 4FIFO队列的类型也是两种,Serial Dispatch Queue和 Concurrent Dispatch Queue,分别为串行队列和产出队列,串行队列中的职责必需比照顺序依次实行,并发队列能够三个线程以并发的样式进行。图片 5串行队列和并发队列上面用代码来看串行队列和并发队列的分别,首先利用串行队列的点子创制多少个异步操作

dispatch_async_f

voiddispatch_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func){ dispatch_continuation_t dc; // No fastpath/slowpath hint because we simply don't know //串行队列,执行dispatch_barrier_async_f,其实最后还是执行任务入队的操作 if (dq->dq_width == 1) { return dispatch_barrier_async_f(dq, ctxt, func); } //从线程私有数据中获取一个dispatch_continuation_t的结构体 dc = fastpath(_dispatch_continuation_alloc_cacheonly; if  { return _dispatch_async_f_slow(dq, ctxt, func); } dc->do_vtable = DISPATCH_OBJ_ASYNC_BIT; dc->dc_func = func; dc->dc_ctxt = ctxt; // No fastpath/slowpath hint because we simply don't know //有目标队列,调用_dispatch_async_f2函数进行转发。 if (dq->do_targetq) { return _dispatch_async_f2; } //全局队列直接进行入队操作 _dispatch_queue_push;}

从下边的源代码中大家得以看看dispatch_async_f粗粗分为两种情景:

  1. 万一是串行队列,调用dispatch_barrier_async_f
  2. 任何队列且有目的队列,调用_dispatch_async_f2
  3. 如假诺全局队列的话,直接开展入队操作。

就算下边分两种景况,然则究竟,它们最后实行都以_dispatch_queue_push来进行入队的操作。

此间有有个别索要在乎下:正是dispatch_continuation_tdo_vtable的赋值景况。

//串行队列,barrierdc->do_vtable = (DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT);//not barrierdc->do_vtable = DISPATCH_OBJ_ASYNC_BIT;

libdispatch任何标记符有各种:

#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 //同步慢

从地点大家得以了然串行队列在异步奉行的时候,通过DISPATCH_OBJ_BARRIER_BIT其一标识符达成窒碍等待的。

随着我们分析下dispatch_barrier_async_f_dispatch_async_f2这多个函数。

dispatch_barrier_async_f的源码如下:

voiddispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func){ dispatch_continuation_t dc; //从线程私有数据中获取一个dispatch_continuation_t的结构体,dispatch_continuation_t中封装了异步执行任务。 dc = fastpath(_dispatch_continuation_alloc_cacheonly; if  { //return _dispatch_barrier_async_f_slow(dq, ctxt, func); //以下是_dispatch_barrier_async_f_slow的具体实现 //如果没有则从堆上获取一个dispatch_continuation_t的结构体 dispatch_continuation_t dc = _dispatch_continuation_alloc_from_heap(); //通过do_vtable区分类型 dc->do_vtable = (DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); //将_dispatch_call_block_and_release作为func方法 dc->dc_func = func; //将传入的block作为上下文 dc->dc_ctxt = ctxt; //入队操作 _dispatch_queue_push; } dc->do_vtable = (DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); dc->dc_func = func; dc->dc_ctxt = ctxt; _dispatch_queue_push;}
6. 主队列 同步试行 (不可能在主队列这么用,死锁)

主线程,同步试行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);

    /*
     NSLog输出
     0========<NSThread: 0x600000263840>{number = 3, name = (null)}
     1========<NSThread: 0x608000078ec0>{number = 1, name = main}
     2========<NSThread: 0x608000078ec0>{number = 1, name = main}
     3========<NSThread: 0x608000078ec0>{number = 1, name = main}
     4========<NSThread: 0x600000263840>{number = 3, name = (null)}
     */
dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ NSLog(@"%@---0", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---1", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---2", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---3", [NSThread currentThread]); });

_dispatch_queue_push

_dispatch_queue_push是一个宏定义,它聊起底会化为推行_dispatch_queue_push_list函数。

#define _dispatch_queue_push _dispatch_queue_push_list, #define _dispatch_queue_push_list _dispatch_trace_queue_push_liststatic inline void_dispatch_trace_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, dispatch_object_t _tail){ if (slowpath(DISPATCH_QUEUE_PUSH_ENABLED { struct dispatch_object_s *dou = _head._do; do { //主要是对dispatch_continuation_s结构体的处理,确保后面的使用。 _dispatch_trace_continuation(dq, dou, DISPATCH_QUEUE_PUSH); } while (dou != _tail._do && (dou = dou->do_next)); } _dispatch_queue_push_list(dq, _head, _tail);}static inline void_dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, dispatch_object_t _tail){ struct dispatch_object_s *prev, *head = _head._do, *tail = _tail._do; tail->do_next = NULL; dispatch_atomic_store_barrier(); //dispatch_atomic_xchg2o实质是调用((typeof__sync_swap),它的定义是将p设为n并返回p操作之前的值。 //dispatch_atomic_xchg2o(dq, dq_items_tail, tail)相当于dq->dq_items_tail = tail,重新设置了队列的尾指针 prev = fastpath(dispatch_atomic_xchg2o(dq, dq_items_tail, tail)); if  { // if we crash here with a value less than 0x1000, then we are at a // known bug in client code for example, see _dispatch_queue_dispose // or _dispatch_atfork_child //prev是原先的队尾,如果队列中有其他的元素,就将压入的对象加在队列的尾部。 prev->do_next = head; } else { //如果队列为空 _dispatch_queue_push_list_slow; }}

_dispatch_queue_push_list_slow

若是队列为空,调用_dispatch_queue_push_list_slow方法。

_dispatch_queue_push_list_slow(dispatch_queue_t dq, struct dispatch_object_s *obj){ //dq->dq_items_head设置为dc,然后唤醒这个队列。因为此时队列为空,没有任务在执行,处于休眠状态,所以需要唤醒 _dispatch_retain; dq->dq_items_head = obj; _dispatch_wakeup; _dispatch_release;}

GCD其余用法

探访输出结果

_dispatch_wakeup

甭管同步依然异步中都调用了_dispatch_wakeup,那么些函数的作用正是提醒当前队列。

_dispatch_wakeup的源码:

dispatch_queue_t_dispatch_wakeup(dispatch_object_t dou){ dispatch_queue_t tq; if (slowpath(DISPATCH_OBJECT_SUSPENDED) { return NULL; } //这里比较隐晦,这里其实是走全局队列的唤醒逻辑调用_dispatch_queue_wakeup_global,如果唤醒失败且对尾指针为空,返回NULL;如果是管理队列的的话,则执行_dispatch_mgr_wakeup函数 if (!dx_probe && !dou._dq->dq_items_tail) { return NULL; } // _dispatch_source_invoke() relies on this testing the whole suspend count // word, not just the lock bit. In other words, no point taking the lock // if the source is suspended or canceled. if (!dispatch_atomic_cmpxchg2o(dou._do, do_suspend_cnt, 0, DISPATCH_OBJECT_SUSPEND_LOCK)) {#if DISPATCH_COCOA_COMPAT if (dou._dq == &_dispatch_main_q) { //传入主队列,会进入到 _dispatch_queue_wakeup_main() 函数中 _dispatch_queue_wakeup_main(); }#endif return NULL; } _dispatch_retain; //有目标队列,继续向目标队列压入这个队列 tq = dou._do->do_targetq; _dispatch_queue_push(tq, dou._do); return tq; // libdispatch does not need this, but the Instrument DTrace // probe does}

从地点的代码能够看出_dispatch_wakeup分成多样情状:

  1. 主队列调用_dispatch_queue_wakeup_main()
  2. 全局队列调用_dispatch_queue_wakeup_global
  3. 其他队列像目的队列压入那些行列,继续做入队操作。
  4. 管住体系调用_dispatch_mgr_wakeup,这里根本是为了dispatch_source而服务的。
void_dispatch_queue_wakeup_main{ kern_return_t kr; dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); //唤醒主线程,这里已经点不进去了,关于主线的唤醒主要靠mach_port和在runloop中注册相对应的source1 kr = _dispatch_send_wakeup_main_thread(main_q_port, 0); switch  { case MACH_SEND_TIMEOUT: case MACH_SEND_TIMED_OUT: case MACH_SEND_INVALID_DEST: break; default: dispatch_assume_zero; break; } _dispatch_safe_fork = false;}

上面提到dx_probe这里走的是大局队列的唤起。前边提到全局队列的do_vtable

static const struct dispatch_queue_vtable_s _dispatch_queue_root_vtable = { .do_type = DISPATCH_QUEUE_GLOBAL_TYPE, .do_kind = "global-queue", .do_debug = dispatch_queue_debug, .do_probe = _dispatch_queue_wakeup_global,};

_dispatch_queue_wakeup_global的源码:

static bool_dispatch_queue_wakeup_global(dispatch_queue_t dq){ static dispatch_once_t pred; struct dispatch_root_queue_context_s *qc = dq->do_ctxt; int r; if (!dq->dq_items_tail) { return false; } _dispatch_safe_fork = false; dispatch_debug_queue(dq, __PRETTY_FUNCTION__); dispatch_once_f(&pred, NULL, _dispatch_root_queues_init);#if HAVE_PTHREAD_WORKQUEUES#if DISPATCH_ENABLE_THREAD_POOL //队列上下文的dgq_kworkqueue存在,则调用pthread_workqueue_additem_np函数,该函数使用workq_kernreturn系统调用,通知workqueue增加应当执行的项目。根据该通知,XNU内核基于系统状态判断是否要生成线程,如果是overcommit优先级的队列,workqueue则始终生成线程,之后线程执行_dispatch_worker_thread2函数。 //工作队列,是一个用于创建内核线程的接口,通过它创建的内核线程来执行内核其他模块排列到队列里的工作。不同优先级的dispatch queue对应着对应优先级的workqueue。GCD初始化的时候,使用pthread_workqueue_create_np创建pthread_workqueue if (qc->dgq_kworkqueue)#endif { if (dispatch_atomic_cmpxchg2o(qc, dgq_pending, 0, 1)) { pthread_workitem_handle_t wh; unsigned int gen_cnt; _dispatch_debug("requesting new worker thread"); r = pthread_workqueue_additem_np(qc->dgq_kworkqueue, _dispatch_worker_thread2, dq, &wh, &gen_cnt); dispatch_assume_zero; } else { _dispatch_debug("work thread request still pending on global " "queue: %p", dq); } goto out; }#endif // HAVE_PTHREAD_WORKQUEUES#if DISPATCH_ENABLE_THREAD_POOL //通过发送一个信号量使线程保活 if (dispatch_semaphore_signal(qc->dgq_thread_mediator)) { goto out; } pthread_t pthr; int t_count; do { t_count = qc->dgq_thread_pool_size; if  { _dispatch_debug("The thread pool is full: %p", dq); goto out; } } while (!dispatch_atomic_cmpxchg2o(qc, dgq_thread_pool_size, t_count, t_count - 1));//如果线程池可用则减1 //这里说明线程池不够用了,使用pthread创建一个线程,并执行_dispatch_worker_thread,_dispatch_worker_thread最终会调用到_dispatch_worker_thread2 while ((r = pthread_create(&pthr, NULL, _dispatch_worker_thread, dq))) { if (r != EAGAIN) { dispatch_assume_zero; } sleep; } //保证pthr能被自动回收掉 r = pthread_detach; dispatch_assume_zero;#endif // DISPATCH_ENABLE_THREAD_POOLout: return false;}

_dispatch_worker_thread2的代码如下:

_dispatch_worker_thread2(void *context){ struct dispatch_object_s *item; dispatch_queue_t dq = context; struct dispatch_root_queue_context_s *qc = dq->do_ctxt; if (_dispatch_thread_getspecific(dispatch_queue_key)) { DISPATCH_CRASH("Premature thread recycling"); } //把dq设置为刚启动的这个线程的TSD _dispatch_thread_setspecific(dispatch_queue_key, dq); qc->dgq_pending = 0;#if DISPATCH_COCOA_COMPAT dispatch_atomic_inc(&_dispatch_worker_threads); // ensure that high-level memory management techniques do not leak/crash if (dispatch_begin_thread_4GC) { dispatch_begin_thread_4GC(); } void *pool = _dispatch_begin_NSAutoReleasePool();#endif#if DISPATCH_PERF_MON uint64_t start = _dispatch_absolute_time();#endif //_dispatch_queue_concurrent_drain_one用来取出队列的一个内容 while ((item = fastpath(_dispatch_queue_concurrent_drain_one { // 用来对取出的内容进行处理(如果是任务,则执行任务) _dispatch_continuation_pop; }#if DISPATCH_PERF_MON _dispatch_queue_merge_stats;#endif#if DISPATCH_COCOA_COMPAT _dispatch_end_NSAutoReleasePool; dispatch_end_thread_4GC(); if (!dispatch_atomic_dec(&_dispatch_worker_threads) && dispatch_no_worker_threads_4GC) { dispatch_no_worker_threads_4GC(); }#endif _dispatch_thread_setspecific(dispatch_queue_key, NULL); _dispatch_force_cache_cleanup();}

那边有七个超级重大的主意:

  1. _dispatch_queue_concurrent_drain_one以此办法用来抽取队列中的贰个剧情。
  2. _dispatch_continuation_pop其一措施用来拍卖抽取的剧情

_dispatch_queue_concurrent_drain_one

struct dispatch_object_s *_dispatch_queue_concurrent_drain_one(dispatch_queue_t dq){ struct dispatch_object_s *head, *next, *const mediator = ~0ul; // The mediator value acts both as a "lock" and a signal head = dispatch_atomic_xchg(&dq->dq_items_head, mediator); if (slowpath(head == NULL)) { //队列是空的 dispatch_atomic_cmpxchg(&dq->dq_items_head, mediator, NULL); _dispatch_debug("no work on global work queue"); return NULL; } if (slowpath(head == mediator)) { // 该线程在现线程竞争中失去了对队列的拥有权,这意味着libdispatch的效率很糟糕, // 这种情况意味着在线程池中有太多的线程,这个时候应该创建一个pengding线程, // 然后退出该线程,内核会在负载减弱的时候创建一个新的线程 _dispatch_queue_wakeup_global; return NULL; } // 在返回之前将head指针的do_next保存下来,如果next为NULL,这意味着item是最后一个 next = fastpath(head->do_next); if (slowpath { dq->dq_items_head = NULL; if (dispatch_atomic_cmpxchg(&dq->dq_items_tail, head, NULL)) { // head 和 tail头尾指针均为空 goto out; } // 此时一定有item,该线程不会等待太久. while (!(next = head->do_next)) { _dispatch_hardware_pause(); } } // 继续调度 dq->dq_items_head = next; _dispatch_queue_wakeup_global;out: // 返回队列的头指针 return head;}

_dispatch_continuation_pop

static inline void_dispatch_continuation_pop(dispatch_object_t dou){ dispatch_continuation_t dc = dou._dc; dispatch_group_t dg; _dispatch_trace_continuation_pop(_dispatch_queue_get_current; //检测是不是队列,如果是,就进入_dispatch_queue_invoke 处理队列 if (DISPATCH_OBJ_IS_VTABLE { return _dispatch_queue_invoke; } // Add the item back to the cache before calling the function. This // allows the 'hot' continuation to be used for a quick callback. // // The ccache version is per-thread. // Therefore, the object has not been reused yet. // This generates better assembly. if dc->do_vtable & DISPATCH_OBJ_ASYNC_BIT) { _dispatch_continuation_free; } //判断是否是group if dc->do_vtable & DISPATCH_OBJ_GROUP_BIT) { dg = dc->dc_group; } else { dg = NULL; } //是任务封装的 dispatch_continuation_t 结构体,直接执行任务。 //到这里我们知道了队列执行的时候,block被调用的时机 _dispatch_client_callout(dc->dc_ctxt, dc->dc_func); if  { //group需要进行调用dispatch_group_leave并释放信号 dispatch_group_leave; _dispatch_release; }}

从地点的函数中能够开采,压入队列的不光是职分,还会有望是队列。假若是队列,直接实施了_dispatch_queue_invoke,不然推行dc->dc_func(dc->dc_ctxt)

_dispatch_queue_invoke

void_dispatch_queue_invoke(dispatch_queue_t dq){ if (!slowpath(DISPATCH_OBJECT_SUSPENDED && fastpath(dispatch_atomic_cmpxchg2o(dq, dq_running, 0, 1))) { dispatch_atomic_acquire_barrier(); dispatch_queue_t otq = dq->do_targetq, tq = NULL; _dispatch_queue_drain; if (dq->do_vtable->do_invoke) { // Assume that object invoke checks it is executing on correct queue tq = dx_invoke; } else if (slowpath(otq != dq->do_targetq)) { // An item on the queue changed the target queue tq = dq->do_targetq; } // We do not need to check the result. // When the suspend-count lock is dropped, then the check will happen. dispatch_atomic_release_barrier(); //dq_running减1,因为任务要么被直接执行了,要么被压到target队列了 dispatch_atomic_dec2o(dq, dq_running); if  { return _dispatch_queue_push; } } dq->do_next = DISPATCH_OBJECT_LISTLESS; if (!dispatch_atomic_sub2o(dq, do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK)) { //队列处于空闲状态,需要唤醒 if (dq->dq_running == 0) { _dispatch_wakeup; // verify that the queue is idle } } //释放队列 _dispatch_release; // added when the queue is put on the list}

现行反革命大家收拾一下队列异步实践的流程,如下图:图片 6dispatch_async

  1. dispatch_queue透过布局体和链表,被完成为FIFO队列。无论串行队列和现身队列,都以切合FIFO的口径。两个的非常重要差距是:试行各样不一致,以至开启线程数分歧。

  2. dispatch_sync函数平常都在当下线程试行,利用与线程绑定的时域信号量来促成串行。

  3. Block并非一向抬高到行列上,而是先构成一个dispatch_continuation布局体。结构体富含了那些Block还恐怕有一部分上下文音讯。队列会将那些dispatch_continuation组织体增添队列的链表中。无论那么些队列是何等类型的,最后都以和全局队列相关的。在大局队列施行Block的时候,libdispatch从全局队列中收取dispatch_continuation,调用pthread_workqueue_additem_np函数,将该全局队列本人、切合其优先级的workqueue消息以至dispatch_continuation布局体的回调函数字传送递给参数。pthread_workqueue_additem_np函数使用workq_kernreturn系统调用,通告workqueue增加应当进行的品类。依照该同志,XNU内核基于系统状态推断是不是要生成线程。假如是overcommit优先级的大局队列workqueue则会始平生成线程。workqueue的线程实践pthread_workqueue函数,该函数调用libdispatch的回调函数。在该函数中实行参加到dispatch_continuation的Block

  4. dispatch_async分发到主队列的天职由Runloop拍卖,而分发到别的队列的天职由线程池管理。

  5. GCD死锁是队列引致的并非线程诱致,原因是_dispatch_barrier_sync_f_slow函数中使用了线程对应的非复信号量而且调用wait方法,进而以致线程死锁。

  6. dispatch_barrier_async适用的景色队列必须是用DISPATCH_QUEUE_CONCURRENT特性成立的行列,而使用全局并发队列的时候,其表现就和dispatch_async一样。原因:dispatch_barrier_async只要传入的是全局队列,在提示队列时会试行_dispatch_queue_wakeup_global函数,其实施效果同dispatch_async肖似,而只倘使自定义的种类的时候,_dispatch_continuation_pop中会实行dispatch_queue_invoke。在while循环中逐一收取职务并调用_dispatch_continuation_redirect函数,使得block并发实施。当境遇DISPATCH_OBJ_BARRIER_BIT标记时,会修改do_suspend_cnt标注以保证持续while循环时直接goto out。barrier block的任务施行完现在_dispatch_queue_class_invoke会将do_suspend_cnt重新载入参数回去,所以barrier block之后的职分会继续推行。

  7. 队列操作和线程开启的关联:dispatch_sync丰裕职务到行列,不会创立新的线程。全体职分都以在当前线程中管理的。dispatch_async累计任务到行列分为两种情景:主队列不创制线程,在主线程中串行实践;全局队列和自定义并行队列依照职务系统调节开拓线程个数;自定义串行队列创立叁个线程,串行实行。

dispatch_after延时

1、time = 0,是直接调用异步dispatch_async
2、time > 0, 只是延时提交block,不是延时即时实践。

    //2秒延时、在主队列
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{

    });
<NSThread: 0x6000002777c0>{number = 3, name = }---0<NSThread: 0x6000002777c0>{number = 3, name = }---1<NSThread: 0x6000002777c0>{number = 3, name = }---2<NSThread: 0x6000002777c0>{number = 3, name = }---3
dispatch_once与dispatch_once_t
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //
    });

1、dispatch_once并不是简简单单的只实行一遍那么简单
2、dispatch_once本质上得以选拔频频呼吁,会对此维护多个伸手链表
3、假如在block实行时期,多次步入调用同类的dispatch_once函数(即单例函数),会引致全部链表Infiniti增进,造成恒久性死锁。

递归互相嵌套,如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ // 链表无限增长
        [self viewDidLoad];
    });
}

dispatch_once源码

static void
dispatch_once_f_slow(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
#if DISPATCH_GATE_USE_FOR_DISPATCH_ONCE
    dispatch_once_gate_t l = (dispatch_once_gate_t)val;

    if (_dispatch_once_gate_tryenter(l)) {
        _dispatch_client_callout(ctxt, func);
        _dispatch_once_gate_broadcast(l);
    } else {
        _dispatch_once_gate_wait(l);
    }
#else
    _dispatch_once_waiter_t volatile *vval = (_dispatch_once_waiter_t*)val;
    struct _dispatch_once_waiter_s dow = { };
    _dispatch_once_waiter_t tail = &dow, next, tmp;
    dispatch_thread_event_t event;


    if (os_atomic_cmpxchg(vval, NULL, tail, acquire)) {

        // 第一次dispatch_once,原子性操作

        // 当前线程
        dow.dow_thread = _dispatch_tid_self();
        // 执行block
        _dispatch_client_callout(ctxt, func);

        // 第一次执行完了,设置token = DISPATCH_ONCE_DONE
        next = (_dispatch_once_waiter_t)_dispatch_once_xchg_done(val);
        while (next != tail) {

            // 继续去下一个
            tmp = (_dispatch_once_waiter_t)_dispatch_wait_until(next->dow_next);
            event = &next->dow_event;
            next = tmp;

            // 信号量  
            _dispatch_thread_event_signal(event);
        }
    } else {

        // 第二次dispatch_once进来
        _dispatch_thread_event_init(&dow.dow_event);
        next = *vval;
        for (;;) {
            if (next == DISPATCH_ONCE_DONE) { // token是否等于DISPATCH_ONCE_DONE
                // 第一次执行完之后,都是走这里
                break;
            }
            // 如果是嵌套使用,第一次没有完成,又要执行一次
            if (os_atomic_cmpxchgv(vval, next, tail, &next, release)) {
                // 原子性
                dow.dow_thread = next->dow_thread;
                dow.dow_next = next;
                if (dow.dow_thread) {
                    pthread_priority_t pp = _dispatch_get_priority();
                    _dispatch_thread_override_start(dow.dow_thread, pp, val);
                }
                // 等待信号量
                _dispatch_thread_event_wait(&dow.dow_event);
                if (dow.dow_thread) {
                    _dispatch_thread_override_end(dow.dow_thread, val);
                }
                break;
            }
        }
        _dispatch_thread_event_destroy(&dow.dow_event);
    }
#endif
}

很分明,这里是同三个线程,而且也着实是依据顺序执行的,那么接下去使用并发队列来操作。

dispatch_apply(count,queue,block(indexState of QatarState of Qatar迭代艺术

该函数按钦定的次数将钦赐的block追加到钦命的行列;使用之处,拥塞当前线程

    NSLog(@"CurrentThread------%@", [NSThread currentThread]);
    //dispatch_queue_t queue = dispatch_get_global_queue(0,0);
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    // 6是次数
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd------%@",index, [NSThread currentThread]);
    });
    /*
     并发队列:开多线程异步执行
     NSLogx信息
     CurrentThread------<NSThread: 0x600000268a40>{number = 3, name = (null)}
     0------<NSThread: 0x600000268a40>{number = 3, name = (null)}
     1------<NSThread: 0x608000266e40>{number = 4, name = (null)}
     2------<NSThread: 0x608000266f00>{number = 5, name = (null)}
     3------<NSThread: 0x608000266f40>{number = 6, name = (null)}
     4------<NSThread: 0x600000268a40>{number = 3, name = (null)}
     5------<NSThread: 0x608000266e40>{number = 4, name = (null)}
     */


    /*
     同步队列:当前线程同步执行
     NSLogx信息
     CurrentThread------<NSThread: 0x608000072c00>{number = 3, name = (null)}
     0------<NSThread: 0x6000000694c0>{number = 1, name = main}
     1------<NSThread: 0x6000000694c0>{number = 1, name = main}
     2------<NSThread: 0x6000000694c0>{number = 1, name = main}
     3------<NSThread: 0x6000000694c0>{number = 1, name = main}
     4------<NSThread: 0x6000000694c0>{number = 1, name = main}
     5------<NSThread: 0x6000000694c0>{number = 1, name = main}
     */

dispatch_apply能幸免线程爆炸,因为GCD会管理现身

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 999; i  ){
      dispatch_async(queue, ^{
         NSLog(@"%d,%@",i,[NSThread currentThread]);// 能开多大线程就开多大线程(几十个)
      });
}

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(999, queue, ^(size_t i){
     NSLog(@"%d,%@",i,[NSThread currentThread]); // 只开一定数量的线程(几个)
});
dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"%@---0", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---1", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---2", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---3", [NSThread currentThread]); });
dispatch_suspend、dispatch_resume (用在dispatch_get_global_queue主队列无效卡塔尔

dispatch_suspend,dispatch_resume提供了“挂起、苏醒”队列的效果,轻松的话,正是能够暂停、苏醒队列上的天职。不过此地的“挂起”,并不可能承保能够致时停止队列上正在运转的block

注意点:

再看看输出结果

1、若是队列未有采取dispatch_suspend,使用dispatch_resume会crash
- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_resume(queue); // crash
}
<NSThread: 0x60400046adc0>{number = 5, name = }---2<NSThread: 0x60400046b000>{number = 3, name = }---0<NSThread: 0x6000004655c0>{number = 4, name = }---1<NSThread: 0x6000004653c0>{number = 6, name = }---3
2、若是queue被挂起,queue销毁时候从不被唤醒,会crash
        dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
        dispatch_suspend(queue);// 如果queue被挂起,queue销毁时候没有被唤醒,会crash

很显著,并从未如约顺序实践,而且不是同二个线程,那就认证了下面所说的串行队列和并发队列的分裂。可是不表示创制了稍微个冒出任务就能够开拓多少个线程,这几个线程数量由XNU内核来决定,将地点的案例调治为8个职分,能够看到有个别线程是再一次的,注脚了尽管一起头就足以开采8个线程,就不会并发这种情况,所以当七个职分完结之后,下叁个职责能够停放实现的线程中,不过和串行队列仍有分别的

3、dispatch_suspend前边实行dispatch_sync,拥塞当前线程,须求任何线程苏醒队列
        queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
        dispatch_suspend(queue);
        // 后面执行dispatch_sync,阻塞当前线程,需要其他线程恢复队列
        dispatch_sync(queue, ^{
            NSLog(@"=====%@",@"111111");
        });
        NSLog(@"=====%@",@"22222");

图片 7多少个任务的面世队列

GCD的队列组dispatch_group_t,其实便是包装了一个十二万分大的非确定性信号量,

注意事项
1、dispatch_group_async(只有async,无sync)等价于{dispatch_group_enter()

  • async}, async调用完了会实施dispatch_group_leave()。
    2、dispatch_group_enter(卡塔尔国就是时限信号量--;
    3、dispatch_group_leave(卡塔尔(قطر‎正是非实信号量
    4、dispatch_group_enter(卡塔尔(قطر‎ 必得运营在 dispatch_group_leave() 之前。
    5、dispatch_group_enter() 和 dispatch_group_leave(卡塔尔 供给成对出现的

      //1.创建队列组
      dispatch_group_t group = dispatch_group_create();
    
      //2.1.全局队列
      dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          for (NSInteger i = 0; i < 3; i  ) {
              NSLog(@"group-01 - %@", [NSThread currentThread]);
          }
      });
    
      //2.2.主队列
      dispatch_group_async(group, dispatch_get_main_queue(), ^{
          for (NSInteger i = 0; i < 3; i  ) {
              NSLog(@"group-02 - %@", [NSThread currentThread]);
          }
      });
    
      //2.3.自建串行队列
      dispatch_group_async(group, dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL), ^{
          for (NSInteger i = 0; i < 3; i  ) {
              NSLog(@"group-03 - %@", [NSThread currentThread]);
          }
      });
    
      //3.都完成后会自动通知,不阻塞当前线程
      dispatch_group_notify(group, dispatch_get_main_queue(), ^{
          NSLog(@"完成 - %@", [NSThread currentThread]);
      });
      NSLog(@"当前线程=====%@",[NSThread currentThread]);
    
       /*
        并行队列、自建串行队列的任务多线程异步执行
        主队列的任务主线程同步执行,且排在全部任务的最后
    
        NSLog信息
        当前线程=====<NSThread: 0x60000007c240>{number = 1, name = main}
        group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
        group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
        group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
        group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
        group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
        group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
        group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
        group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
        group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
        完成 - <NSThread: 0x60000007c240>{number = 1, name = main}
        */
    

先是会开辟4条线程管理义务,将task0-task3异步情势放入线程中试行,假定task0首先实现,并发队列中抽出task4放入线程3中施行,就这样推算,所以也就产生了有一些线程是再一次的案由。

手动标志group实现
  • dispatch_group_enter(group)
  • dispatch_group_leave(group);

DISPATCH MAIN QUEUE/DISPATCH GLOBAL QUEUE咱俩一贯常用的差不离是那三种,前者是在主线程试行的queue,由于主线程独有二个,自然是个串行队列,而global_queue是个并发队列,这么些行列有4个优先级,分别是低优先级(DISPATCH_QUEUE_PRIORITY_LOW卡塔尔国,暗中同意优先级(DISPATCH_QUEUE_PRIORITY_DEFAULT卡塔尔(قطر‎,高优先级(DISPATCH_QUEUE_PRIORITY_HIGH卡塔尔(قطر‎,和后台优先级(DISPATCH_QUEUE_PRIORITY_BACKGROUND卡塔尔国,代码如下

dispatch_group_notify(不打断)想当与把block职务加在最终
NSLog(@"start");
    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();
    for (int i=0; i< 5; i  ) {
        dispatch_group_enter(group); // enter
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // something
            NSLog(@"something===%zd",i);
            dispatch_group_leave(group); // eave
        });
    }
    // 都完成后会自动通知,不阻塞当前线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"完成 - %@", [NSThread currentThread]);
    });
    NSLog(@"end");

     /*
      NSLog信息:
      start
      end
      something===1
      something===0
      something===2
      something===3
      something===4
      完成 - <NSThread: 0x60800006e900>{number = 1, name = main}
      */
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_group_wait正是伺机group的能量信号量回到开端值(窒碍当前线程)
    NSLog(@"start");
    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();

    for (int i=0; i< 5; i  ) {

        dispatch_group_enter(group); // enter

        dispatch_async(dispatch_get_global_queue(0, 0), ^{

            // something

            [NSThread sleepForTimeInterval:2];
            NSLog(@"something===%zd",i);
            dispatch_group_leave(group); // eave
        });

    }
    // 阻塞当前线程的、等待5秒
    dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
    dispatch_group_wait(group, waitTime);//特殊值有:DISPATCH_TIME_FOREVER是无限等,DISPATCH_TIME_NOW是不等
    NSLog(@"end");


     /*
      等待时间 < 执行需要时间
      NSLog信息:
      start
      end
      something===0
      something===1
      something===3
      something===2
      something===4
      */

    /*
     等待时间 > 执行需要时间
     NSLog信息:
     start
     something===1
     something===0
     something===4
     something===2
     something===3
     end
     */

前边已经涉及一些关于GCD的api,下边会详细的深究这几个api的效用dispatch_queue_create

dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait

时限信号量是决定任务奉行的要害尺度,当时域信号量为0时,全数任务等待,确定性信号量越大,允许可并行执行的天职位数量量越来越多。

  • dispatch_semaphore_create(long valueState of Qatar;成立非非确定性信号量,初步值不可能小于0;value实信号数值
  • dispatch_semaphore_wait(semaphore, timeout卡塔尔;等待减弱实信号量,也便是非时域信号量-1;timeout不是调用dispatch_semaphore_wait后等候的时间,而是非非确定性信号量创设后的时刻
  • dispatch_semaphore_signal(semaphoreState of Qatar;提升功率信号量,也等于实信号量 1;
  • dispatch_semaphore_wait和dispatch_semaphore_signal常常配成对使用。
    // 相当于控制新建的线程数
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    for (int i=0; i< 10; i  ) {

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{

            [NSThread sleepForTimeInterval:1];
            NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });

    }


     /*
      dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
      只有5条线程
      NSLog信息:
      第0次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
      第1次_<NSThread: 0x600000269240>{number = 6, name = (null)}
      第3次_<NSThread: 0x600000269100>{number = 5, name = (null)}
      第2次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
      第4次_<NSThread: 0x600000269780>{number = 7, name = (null)}
      第8次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
      第7次_<NSThread: 0x600000269100>{number = 5, name = (null)}
      第6次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
      第5次_<NSThread: 0x600000269240>{number = 6, name = (null)}
      第9次_<NSThread: 0x600000269780>{number = 7, name = (null)}
      */

    /*
     dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
     10条线程
     NSLog信息:
     第2次_<NSThread: 0x6080000661c0>{number = 3, name = (null)}
     第4次_<NSThread: 0x608000073e40>{number = 7, name = (null)}
     第1次_<NSThread: 0x600000079dc0>{number = 4, name = (null)}
     第5次_<NSThread: 0x6000000721c0>{number = 8, name = (null)}
     第3次_<NSThread: 0x608000073dc0>{number = 6, name = (null)}
     第0次_<NSThread: 0x608000073d40>{number = 5, name = (null)}
     第6次_<NSThread: 0x608000073d80>{number = 9, name = (null)}
     第9次_<NSThread: 0x608000073e00>{number = 10, name = (null)}
     第7次_<NSThread: 0x6000000717c0>{number = 11, name = (null)}
     第8次_<NSThread: 0x600000066b40>{number = 12, name = (null)}
     */
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.example.gcdDeo",NULL);
dispatch_barrier_async、dispatch_barrier_sync (承前启后--用于自建的相互队列)

保证在此以前的职务都早早自身实施,从此以后的天职也迟于本身施行。
dispatch_barrier_async 不封堵当前线程;
dispatch_barrier_sync 堵塞当前线程;

注意:dispatch_barrier_(a卡塔尔国sync只在大团结创制的并发队列上有效,在全局(Global卡塔尔国并发队列、串行队列上,效果跟dispatch_(a卡塔尔(قطر‎sync效果相符。

- (void)test{

    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    dispatch_async(globalQueue, ^{
        NSLog(@"任务1");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务2");
    });
    dispatch_barrier_async(globalQueue, ^{
        NSLog(@"任务barrier");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务3");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务4");
    });
    /*
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431532] 任务2
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431535] 任务1
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431533] 任务barrier
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431551] 任务3
     2017-09-02 21:03:40.256 NSThreadTest[28856:21431550] 任务4
     */
}

GCD创建Timer

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //1.创建一个GCD定时器
    /*
     第一个参数:表明创建的是一个定时器
     第四个参数:队列
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 需要对timer进行强引用,保证其不会被释放掉,才会按时调用block块
    // 局部变量,让指针强引用
    self.timer = timer;
    //2.设置定时器的开始时间,间隔时间,精准度
    /*
     第1个参数:要给哪个定时器设置
     第2个参数:开始时间
     第3个参数:间隔时间
     第4个参数:精准度 一般为0 在允许范围内增加误差可提高程序的性能
     GCD的单位是纳秒 所以要*NSEC_PER_SEC
     */
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    //3.设置定时器要执行的事情
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"---%@--",[NSThread currentThread]);
    });
    // 启动
    dispatch_resume(timer);
}

这里是创设贰个queue的格局,重回类型为dispatch_queue_t,方法包罗多个参数,一个是队列名,推荐应用应用程序ID逆序的域名情势,前面参数表示生成的连串类型,一种为DISPATCH_QUEUE_SE福睿斯IAL为串行队列,这里也得以应用NULL,那几个宏定义的值正是NULL,另一种是DISPATCH_QUEUE_CONCUTiguanRENT表示并发队列,这里即便串行队列中的任务是按梯次施行的,不过只要创设五个串行队列,是会并行管理。

GCD各类死锁的状态

图片 8并行实施的多个Serial Dispatch Queue

1、最遍布的
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    NSLog(@"2========%@",[NSThread currentThread]);

    /*
     NSLog信息
     0========<NSThread: 0x60000007c5c0>{number = 1, name = main}

     解析
     1.viewDidLoad是主队列,dispatch_sync也属于主队列,
     2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完,dispatch_sync需要等待viewDidLoad执行完,这就死锁了
     */

}

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
    // 改成dispatch_get_global_queue或者new出来的队列
    dispatch_sync(dispatch_get_global_queue(0,0), ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    NSLog(@"2========%@",[NSThread currentThread]);

    /*
     NSLog信息
     0========<NSThread: 0x6000000690c0>{number = 1, name = main}
     1========<NSThread: 0x6000000690c0>{number = 1, name = main}
     2========<NSThread: 0x6000000690c0>{number = 1, name = main}

     解析
     1.viewDidLoad是主队列,dispatch_sync是global_queue,不在同一队列
     2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完返回,但是dispatch_sync不需要等待viewDidLoad执行完,立即执行完返回
     */

}

是因为多个队列只操作二个线程,所以在实施一些比较重大的操作时,尽量采取串行队列,防止形成数据矛盾。

本文由68399皇家赌场发布于呼叫中心培训课程,转载请注明出处:深入理解GCD之dispatch_queue,GCD的使用和原理

关键词: 68399皇家赌场 GCD dispatch_que iOS基本功 OCFoundatio

最火资讯