Dispatch

作者: edison0428 | 来源:发表于2017-07-06 18:34 被阅读123次
    • 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

    相关文章

      网友评论

        本文标题:Dispatch

        本文链接:https://www.haomeiwen.com/subject/cudqhxtx.html