Dispatch

  • Managing Units of Work(管理工作单位)

调度块允许您直接配置队列中各个工作单元的属性。它们还允许您处理个别工作单位,以等待其完成,并通知其完成和/或取消它们

  • dispatch_block_t

1.这是提交给dispatch队列的块的原型,它不带参数,也没有返回值。
2.创建block的方式有两种

  • 2.1:dispatch_block_create

2.1.1

  • 2.2:dispatch_block_create_with_qos_class
dispatch_block_t blockT=dispatch_block_create(0, ^{
        NSLog(@"11111");
    });
    NSLog(@"22222");
    dispatch_queue_t myQueue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(myQueue, blockT);
    NSLog(@"33333");
    
    //第三个参数设置成1 居然在下面dispatch_async(myQueue, blockY);处奔溃,不知道是不是死锁,暂不知原因
    dispatch_block_t blockY=dispatch_block_create_with_qos_class(1, QOS_CLASS_USER_INTERACTIVE, 0, ^{
        NSLog(@"44444");
    });
    
    dispatch_async(myQueue, blockY);
    NSLog(@"5555");

参数:
参数1:是一个dispatch_block_flags枚举
DISPATCH_BLOCK_BARRIER
DISPATCH_BLOCK_DETACHED
DISPATCH_BLOCK_ASSIGN_CURRENT
DISPATCH_BLOCK_NO_QOS_CLASS
DISPATCH_BLOCK_INHERIT_QOS_CLASS
DISPATCH_BLOCK_ENFORCE_QOS_CLASS
并不太清楚具体原因
参数2.而 qos_class_t 是一种枚举,有以下类型:
QOS_CLASS_USER_INTERACTIVE: user interactive 等级表示任务需要被立即执行,用来在响应事件之后更新 UI,来提供好的用户体验。这个等级最好保持小规模。
QOS_CLASS_USER_INITIATED: user initiated 等级表示任务由 UI 发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
QOS_CLASS_DEFAULT: default 默认优先级
QOS_CLASS_UTILITY: utility 等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
QOS_CLASS_BACKGROUND: background 等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
QOS_CLASS_UNSPECIFIED: unspecified 未指明
参数3:

  • dispatch_function_t

1.提交调度队列的函数原型
2.函数可以接受一个dispatch_function_t类型作为参数,同时接受一个指向您提供上下文数据。调用调度函数时,将上下文数据的指针作为参数传递给函数。对上下文数据的指针未经修改而传递给您的函数,您有责任确保指针是有效的

  • dispatch_block_perform

1.从指定的块和标志创建、同步执行和释放调度块
2.在不需要对指定块的堆或一个新块对象的分配进行复制的情况下,可以更有效地实现此功能
3.相当于如下代码

//调用
NSLog(@"11111");
    dispatch_block_perform(0, ^{
       
        NSLog(@"2222");
    });
    NSLog(@"3333");
//dispatch_block_perform相当于如下
dispatch_block_t b = dispatch_block_create(flags, block);
b();
Block_release(b)
  • dispatch_block_wait

1.同步地等待,直到执行指定的调度块已经完成,或者直到指定的超时已经过去。
2.该函数会阻塞当前线程进行等待。传入需要设置的 block 和等待时间 timeout 。timeout 参数表示函数在等待 block 执行完毕时,应该等待多久。如果执行 block 所需的时间小于 timeout ,则返回 0,否则返回非 0 值。此参数也可以取常量DISPATCH_TIME_FOREVER ,这表示函数会一直等待 block 执行完,而不会超时。可以使用 dispatch_time 函数和 DISPATCH_TIME_NOW 常量来方便的设置具体的超时时间。
3.如果 block 执行完成, dispatch_block_wait 就会立即返回。不能使用 dispatch_block_wait 来等待同一个 block 的多次执行全部结束;这种情况可以考虑使用dispatch_group_wait 来解决。也不能在多个线程中,同时等待同一个 block 的结束。同一个 block 只能执行一次,被等待一次。
4.注意:因为 dispatch_block_wait 会阻塞当前线程,所以不应该放在主线程中调用

dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(concurrentQuene, ^{
        dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_block_t block = dispatch_block_create(0, ^{
            NSLog(@"开始执行");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"结束执行");
        });
        
        dispatch_async(allTasksQueue, block);
        // 等待时长,10s 之后超时
        dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
        long resutl = dispatch_block_wait(block, timeout);
        if (resutl == 0) {
            NSLog(@"执行成功");
        } else {
            NSLog(@"执行超时");
        }
    });
  • dispatch_block_notify

1.可理解为监听
2.在指定的调度块执行完成时,调度一个通知块提交给队列
3.该函数接收三个参数,第一个参数是需要监视的 block,第二个参数是监听的 block 执行结束之后要提交执行的队列 queue,第三个参数是待加入到队列中的 block。 和 dispatch_block_wait 的不同之处在于:dispatch_block_notify 函数不会阻塞当前线程

  • dispatch_block_cancel

1.这个函数用异步的方式取消指定的 block

dispatch_queue_t myQueue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
    // 耗时任务
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"开始第一个任务:%d",[NSThread isMainThread]);
        [NSThread sleepForTimeInterval:1.5f];
        NSLog(@"结束第一个任务");
    });
    // 耗时任务
    dispatch_block_t secBlock = dispatch_block_create(0, ^{
        NSLog(@"开始第二个任务:%d",[NSThread isMainThread]);
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"结束第二个任务");
    });
    dispatch_async(myQueue, firstBlock);
    dispatch_async(myQueue, secBlock);
    // 等待 1s,让第一个任务开始运行,因为myQueue是串行队列,遵守fifo先进先出的规则,所以必须先执行完block1,才能执行block2
    [NSThread sleepForTimeInterval:1];
    NSLog(@"休眠:%d",[NSThread isMainThread]);
    dispatch_block_cancel(firstBlock);
    NSLog(@"准备取消第一个任务");
    dispatch_block_cancel(secBlock);
    NSLog(@"准备取消第二个任务");
    /*
     打印的结果为:
     2017-07-06 18:32:04.046 多线程-GCD[6427:205689] 开始第一个任务:0
     2017-07-06 18:32:05.047 多线程-GCD[6427:205642] 休眠:1
     2017-07-06 18:32:05.047 多线程-GCD[6427:205642] 准备取消第一个任务
     2017-07-06 18:32:05.047 多线程-GCD[6427:205642] 准备取消第二个任务
     2017-07-06 18:32:05.547 多线程-GCD[6427:205689] 结束第一个任务
     可见 dispatch_block_cancel 对已经在执行的任务不起作用,只能取消尚未执行的任务
     */
  • dispatch_block_testcancel

1.测试给定的调度块是否已被取消
2.如果取消调度块,则返回一个非零值,否则为零

  • Prioritizing Work and Specifying Quality of Service(优先工作和指定服务质量

)

  • Dispatch Queue Priorities

1.用于选择适当的全局并发队列(global concurrent queue)
1.1 DISPATCH_QUEUE_PRIORITY_HIGH:这个常数映射到 QOS_CLASS_USER_INITIATED类
发送到队列的项目以高优先级运行;队列在任何默认优先级或低优先级队列之前调度执行
1.2 DISPATCH_QUEUE_PRIORITY_DEFAULT:这个常数映射到 QOS_CLASS_DEFAULT类
发送到队列的项目以默认优先级运行;队列在所有高优先级队列已排定之后执行调度,但在调度任何低优先级队列之前
1.3 DISPATCH_QUEUE_PRIORITY_LOW:这个常数映射到 QOS_CLASS_UTILITY类
发送到队列的项目以低优先级运行;队列在所有默认优先级和高优先级队列已排定之后执行调度
1.4 DISPATCH_QUEUE_PRIORITY_BACKGROUND:这个常数映射到QOS_CLASS_BACKGROUND类
发送到队列的项目以后台优先级运行;队列在所有高优先级队列被调度后执行调度,系统在其优先级为后台状态设置的线程上运行项目。这样一个线程的优先级最低,任何磁盘I/O是节流,减少对系统的影响

2.在MacOS 10.10以后,利用QoS类代替。有关更多信息,请参见服务质量类(QoS)

  • dispatch_qos_class_t
  • dispatch_queue_priority_t
  • dispatch_queue_get_qos_class
  • dispatch_queue_attr_make_with_qos_class

如上这几个,官方文档并没有做过多介绍,了解就行

  • Using Dispatch Groups(利用调度组)

分组块允许聚合同步。您的应用程序可以提交多个块,并在它们全部完成时跟踪它们,即使它们可能在不同的队列上运行。当所有指定的任务都完成后,你再需要做什么行为时,这种类是有帮助的

  • dispatch_group_t

1.提交一组块对象给队做为异步调用
2.调度组是监视一组块的机制。应用程序可以根据需要同步或异步监视组中的块。通过扩展,一个组可以用于对依赖于其他任务完成的代码进行同步。
注意,组中的块可以在不同的队列上运行,并且每个单独的块可以向组添加更多的块

  • dispatch_group_async

1.将block(任务)提交到指定的队列中,并且将次任务放到(关联)指定的group,block将异步执行
2.参数
group:要关联的组。该组由系统保留,直到该块运行完成为止。此参数不能为空
queue:为异步调用提交块对象的调度队列。队列被系统保留,直到该块运行到完成为止。此参数不能为空
block:异步执行的块对象。这个函数执行代表调用者的block_copy和block_release

  • dispatch_group_async_f

1.这个方法跟dispatch_group_async差不多,只是第三个参数为c语言函数

  • dispatch_group_notify_f

1.当group中的任务都完成以后会执行block.注意这句代码要加到所有任务提交之后才管用.参数queue代表block会提交到哪个队列中

dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t conCurrentQueue =  dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, serialQueue, ^{
        NSLog(@"串行队列任务一开始");
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"串行队列任务一快结束:%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, serialQueue, ^{
        NSLog(@"串行队列任务二开始");
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"串行队列任务二快结束:%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, conCurrentQueue, ^{
       
        NSLog(@"并行队列任务二开始");
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"并行队列任务二快结束:%@",[NSThread currentThread]);
        
    });
    dispatch_group_notify(group, conCurrentQueue, ^{
       
        NSLog(@"被通知的并行队列任务三");
    });  
//结果时“被通知的并行队列任务三”是在所有任务都执行完成后才执行
  • dispatch_group_wait

1.为先前提交的块对象同步地等待完成;如果在指定的超时时间结束之前块没有完成,则返回
2.同步等待会阻塞线程,跟dispatch_block_wait原理一样

dispatch_queue_t conCurrentQueue =  dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, conCurrentQueue, ^{
        NSLog(@"任务一开始");
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"任务一快结束");
    });
    dispatch_group_async(group, conCurrentQueue, ^{
        NSLog(@"任务二开始");
        [NSThread sleepForTimeInterval:6];
        
        NSLog(@"任务二快结束");
    });
    
    dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
    long result=dispatch_group_wait(group, time);
    if (result==0) {
        NSLog(@"组的block全部执行完成");
    }
    else{
        NSLog(@"组的block没有全部执行完成,是timeout返回");
    }
    NSLog(@"-----------");
  • dispatch_group_enter

1.用这个方法指定一个操作将要加到group中,用来替代dispatch_group_async,注意它只能和dispatch_group_leave配对使用.
2.这种方式比dispatch_group_async更加灵活.比如我们可以在任务的完成回调里面写dispatch_group_leave()

dispatch_queue_t conCurrentQueue =  dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    dispatch_async(conCurrentQueue, ^{
        NSLog(@"任务一开始");
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"任务一快结束");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(conCurrentQueue, ^{
        NSLog(@"任务二开始");
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"任务二快结束");
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, conCurrentQueue, ^{
        NSLog(@"被通知任务开始");
    });
    NSLog(@"-----");
  • Using Dispatch Semaphores(利用调度信号量)

关于信号量的解释,借别人的一个例子:
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal。就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(value:Int))),调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主。没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,所以就一直等下去

  • dispatch_semaphore_create
  1. 该函数使用一个初始值创建一个dispatch_semaphore_t类型的信号量,注意:这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL
    2.参数就是信号量的初始值
  • dispatch_semaphore_wait

1.等待信号量,该函数会使传入的信号量dsema的值减1
2.函数的作用:如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,需要传入对应的类型参数),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1, 如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。

  • dispatch_semaphore_signal

1.当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒.

  • 关于信号量的用途和总结

0.注意,正常的使用顺序是先降低然后再提高,这两个dispatch_semaphore_wait 和dispatch_semaphore_signal函数通常成对使用
1.如果初始化信号量为1的话,相当于给线程加锁,因为信号量为1,不管什么情况下,都会保证只有一个线程再访问此关键代码,是线程安全
2.可以相当于设置最大并发数量,比如初始化的信号量为4,就算有五个线程来了,最多的情况也只会有四个同时进行
3.加锁


WechatIMG1.jpeg

如上所示,crash的原因翻译过来是:就是说你malloc分配的内存赋值给了一个已经被释放的指针(此指针已不存在),其实我暂时不明白为什么会这样报错,我的理解是也是因为多线程同时写数组的的问题
修改成如下就行

dispatch_queue_t myQueue = dispatch_queue_create("MyQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    NSMutableArray * array = [[NSMutableArray alloc] init];
    for (int i =1; i<100; i++) {
        dispatch_async(myQueue, ^{
           
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            [array addObject:[NSString stringWithFormat:@"%d",i]];
            dispatch_semaphore_signal(semaphore);
        });
    }

4.设置多线程最大并发数

//crate的value表示,最多几个资源可访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //任务1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 1");
        sleep(1);
        NSLog(@"complete task 1");
        dispatch_semaphore_signal(semaphore);
    });
    //任务2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 2");
        sleep(1);
        NSLog(@"complete task 2");
        dispatch_semaphore_signal(semaphore);
    });
    //任务3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 3");
        sleep(1);
        NSLog(@"complete task 3");
        dispatch_semaphore_signal(semaphore);
    });
    /*
     2017-07-07 17:07:41.632 多线程-GCD[1125:163408] run task 1
     2017-07-07 17:07:41.632 多线程-GCD[1125:163421] run task 2
     2017-07-07 17:07:42.634 多线程-GCD[1125:163408] complete task 1
     2017-07-07 17:07:42.635 多线程-GCD[1125:163421] complete task 2
     2017-07-07 17:07:42.635 多线程-GCD[1125:163405] run task 3
     2017-07-07 17:07:43.640 多线程-GCD[1125:163405] complete task 3
     
     总结:由于设定的信号值为2,先执行两个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过2
     这也相当于设置了多线程的最大并发数
     */
  • Using Dispatch Barriers(栅栏)

对于栅栏的理解:假设我们原先有10个任务要执行,我们现在要插入一个任务5,这个任务5要在1,2,3,4,都并发执行完了之后才能执行,而6,7,8,9,10号任务要在这个任务5结束后才允许并发。这是就需要用到Barriers,当然group也可以实现,但是这个方法更好,关于栅栏的只能是同一个队列,而group 可以是不同队列,栅栏最好别用系统的全部队列而是使用自己的全部队列

  • dispatch_barrier_async

1.提交异步执行的障碍块并立即返回,不会阻塞线程
2.该方法的功能跟dispatch_async类似

  • dispatch_barrier_async_f

1.跟dispatch_barrier_async方法一样,只会提交的是c函数

  • dispatch_barrier_sync

1.提交一个用于执行的屏障块对象,并等待该块完成
2.该方法功能跟dispatch_sync类似

  • dispatch_barrier_sync_f

1.跟dispatch_barrier_async方法一样,只会提交的是c函数

  • Managing Dispatch Sources

这个管理类对于我而言还是很少用到的,不过有个确实经常用到的就是 time

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,423评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,339评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,241评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,503评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,824评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,262评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,615评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,337评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,989评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,300评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,829评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,193评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,753评论 3 230
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,970评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,708评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,295评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,207评论 2 258

推荐阅读更多精彩内容