• GCD


    GCD


    OS X 10.8或iOS 6以及之后版本中使用,Dispatch Queue将会由ARC自动管理,不需要手动释放

    队列

    分为串行队列和并发队列

    • 将多个任务提交给串行队列,多个任务只能按顺序执行,前一个任务执行完,才能开始下一个任务
    • 将多个任务交给并发队列,并发队列可以按FIFO的顺序启动多个任务,任务完成顺序按任务和系统决定

    获取队列:

    • dispatch_get_main_queue()
      • 获取主线程关联串行队列
    • dispatch_get_current_queue()
      • 获取当前执行代码所在队列
    • dispatch_get_global_queue(long identifier, unsigned long flags)
      • 获取系统的全局并发队列
      • 第一个参数接受一下四个优先级
        • DISPATCH_QUEUE_PRIORITY_HIGH:
        • DISPATCH_QUEUE_PRIORITY_DEFAULT:
        • DISPATCH_QUEUE_PRIORITY_LOW:
        • DISPATCH_QUEUE_PRIORITY_BACKGROUND:
      • 第二个参数一般传入0

    创建队列:

    • dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
      • 第一个参数表示队列对应字符串标签
      • 第二个参数指定队列类型,分为:
        • DISPATCH_QUEUE_SERIAL 串行
        • DISPATCH_QUEUE_CONCURRENT 并发

    获取队列相关信息

    • dispatch_queue_get_label(dispatch_queue_t queue);
      • 获取队列对应的标签

    提交任务

    • dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
      • 将代码块以异步方式提交给指定队列
    • dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
      • 将代码块以同步方式提交给指定队列
      • 先后提交的两个代码块(即使提交给并发队列),前一个执行完才会执行下一个
    • dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
      • 将代码块以异步方式提交给指定队列,并在dispatch_time指定的时间开始执行
    • dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
      • 将代码块以异步方式提交给指定队列,重复执行代码
      • 第一个参数指定重复几次
      • 第三个参数 block代码块的 size_t表示当前正在执行第几次
    • dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
      • 在任务提交给队列,在应用的某个生命周期内只执行一次
      • 第一个参数表示代码块是否已经执行过

    dispatch_group

    • void dispatch_group_notify(dispatch_group_t group,
      dispatch_queue_t queue,
      dispatch_block_t block);

      • group中所有代码块执行完之后执行
    • long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

      • 返回值表示经过指定的等待时间,属于这个group的任务是否已经全部执行完,如果是则返回0,否则返回非0。
      • 第一个参数表示等待的group
      • 第二个参数则表示等待时间,有两个特殊值
        • DISPATCH_TIME_NOW 表示立刻检查属于这个group的任务是否已经完成
        • DISPATCH_TIME_FOREVER 表示一直等到属于这个group的任务全部完成。

    例子:

    let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    let group = dispatch_group_create()
    
    dispatch_group_async(group, globalQueue) { () -> Void in
    println("1")
    }
    dispatch_group_async(group, globalQueue) { () -> Void in
    println("2")
    }
    dispatch_group_async(group, globalQueue) { () -> Void in
    println("3")
    }
    dispatch_group_notify(group, globalQueue) { () -> Void in
    println("completed")
    }
    

    输出的顺序与添加进队列的顺序无关,因为队列是Concurrent Dispatch Queue,但“completed”的输出一定是在最后的

    let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    let group = dispatch_group_create()
    
    dispatch_group_async(group, globalQueue) { () -> Void in
    println("1")
    }
    dispatch_group_async(group, globalQueue) { () -> Void in
    println("2")
    }
    dispatch_group_async(group, globalQueue) { () -> Void in
    println("3")
    }
    //使用dispatch_group_wait函数
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
    println("completed")
    

    暂停和恢复

    这些函数不会影响到队列中已经执行的任务,队列暂停后,已经添加到队列中但还没有执行的任务不会执行,直到队列被恢复

    • dispatch_suspend(queue) //暂停某个队列
    • dispatch_resume(queue) //恢复某个队列

    dispatch_barrier_async

    我们知道数据在写入时,不能在其他线程读取或写入。但是多个线程同时读取数据是没有问题的。所以我们可以把读取任务放入并行队列,把写入任务放入串行队列,并且保证写入任务执行过程中没有读取任务可以执行。

    这样的需求比较常见,GCD提供了一个非常简单的解决办法——dispatch_barrier_async

    假设我们有四个读取任务,在第二三个任务之间有一个写入任务,代码大概是这样:

    let queue = dispatch_queue_create("com.gcd.kt", DISPATCH_QUEUE_CONCURRENT)
    
    dispatch_async(queue, block1_for_reading)  
    dispatch_async(queue, block2_for_reading)
    
    /*
    	这里插入写入任务,比如:
    	dispatch_async(queue, block_for_writing)
    */
    
    dispatch_async(queue, block3_for_reading)  
    dispatch_async(queue, block4_for_reading) 
    

    如果代码这样写,由于这几个block是并发执行,就有可能在前两个block中读取到已经修改了的数据。如果是有多写入任务,那问题更严重,可能会有数据竞争。

    如果使用dispatch_barrier_async函数,代码就可以这么写:

    dispatch_async(queue, block1_for_reading)  
    dispatch_async(queue, block2_for_reading)
    
    dispatch_barrier_async(queue, block_for_writing)
    
    dispatch_async(queue, block3_for_reading)  
    dispatch_async(queue, block4_for_reading)
    

    dispatch_barrier_async会把并行队列的运行周期分为这三个过程:

    1. 首先等目前追加到并行队列中所有任务都执行完成
    2. 开始执行dispatch_barrier_async中的任务,这时候即使向并行队列提交任务,也不会执行
    3. dispatch_barrier_async中的任务执行完成后,并行队列恢复正常。

    总的来说,dispatch_barrier_async起到了“承上启下”的作用。它保证此前的任务都先于自己执行,此后的任务也迟于自己执行。正如barrier的含义一样,它起到了一个栅栏、或是分水岭的作用。

    这样一来,使用并行队列和dispatc_barrier_async方法,就可以高效的进行数据和文件读写了。


    信号量

    dispatch_semaphore

    首先介绍一下信号量(semaphore)的概念。信号量是持有计数的信号,不过这么解释等于没解释。我们举个生活中的例子来看看。

    假设有一个房子,它对应进程的概念,房子里的人就对应着线程。一个进程可以包括多个线程。这个房子(进程)有很多资源,比如花园、客厅等,是所有人(线程)共享的。

    但是有些地方,比如卧室,最多只有两个人能进去睡觉。怎么办呢,在卧室门口挂上两把钥匙。进去的人(线程)拿着钥匙进去,没有钥匙就不能进去,出来的时候把钥匙放回门口。

    这时候,门口的钥匙数量就称为信号量(Semaphore)。很明显,信号量为0时需要等待,信号量不为零时,减去1而且不等待。

    在GCD中,创建信号量的语法如下:

    var semaphore = dispatch_semaphore_create(2)
    

    这句代码通过dispatch_semaphore_create方法创建一个信号量并设置初始值为2。然后就可以调用dispatch_semaphore_wait方法了。

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
    

    dispatch_semaphore_wait方法表示一直等待直到信号量的值大于等于一,当这个方法执行后,会把第一个信号量参数的值减1。

    第二个参数是一个dispatch_time_t类型的时间,它表示这个方法最大的等待时间。这在第一章中已经讲过,比如
    DISPATCH_TIME_FOREVER表示永久等待。

    返回值也和dispatch_group_wait方法一样,返回0表示在规定的等待时间内第一个参数信号量的值已经大于等于1,否则表示已超过规定等待时间,但信号量的值还是0。

    dispatch_semaphore_wait方法返回0,因为此时的信号量的值大于等于一,任务获得了可以执行的权限。这时候我们就可以安全的执行需要进行排他控制的任务了。

    任务结束时还需要调用
    dispatch_semaphore_signal()方法,将信号量的值加1。这类似于之前所说的,从卧室出来要把锁放回门上,否则后来的人就无法进入了。

    我们来看一个完整的例子:

    var semaphore = dispatch_semaphore_create(1)  
    let queue = dispatch_queue_create("com.gcd.kt", DISPATCH_QUEUE_CONCURRENT)  
    var array: [Int] = []
    
    for i in 1...100000 {  
    	dispatch_async(queue, { () -> Void in
        /*
            某个线程执行到这里,如果信号量值为1,那么wait方法返回1,开始执行接下来的操作。
            与此同时,因为信号量变为0,其它执行到这里的线程都必须等待
        */
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
    
        /*
            执行了wait方法后,信号量的值变成了0。可以进行接下来的操作。
            这时候其它线程都得等待wait方法返回。
            可以对array修改的线程在任意时刻都只有一个,可以安全的修改array
        */
        array.append(i)
    
        /*
            排他操作执行结束,记得要调用signal方法,把信号量的值加1。
            这样,如果有别的线程在等待wait函数返回,就由最先等待的线程执行。
        */
        dispatch_semaphore_signal(semaphore)
    })
    }
    

    计时器

    NSTimeInterval period = 1.0; //设置时间间隔
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行
    
    dispatch_source_set_event_handler(_timer, ^{
    
    //在这里执行事件
    
    });
    
    dispatch_resume(_timer);
  • 相关阅读:
    AndroidWear开发之下载SDK[Android W/Android L]
    Androidの共享登录之方案研究
    AndroidのUI体验之ImmersiveMode沉浸模式
    谷歌Volley网络框架讲解——HttpStack及其实现类
    谷歌Volley网络框架讲解——网络枢纽
    05-使用jQuery操作input的values
    02-jQuery的选择器
    01-jQuery的介绍
    07-BOM client offset scroll 的系列
    06-js中的中的面向对象 定时器
  • 原文地址:https://www.cnblogs.com/sunyanyan/p/5320249.html
Copyright © 2020-2023  润新知