GCD

这是一篇我关于 GCD 的使用以及学习的总结文章。持续更新。
感谢诸多大神在此之前写的各类文章,如果可以,我会尽量把他们添加到附录中,如有遗漏,感谢提醒补充和讨论。
2016-08-06


成果

假如你对我的废话没兴趣,那么看到这里就够了。这是我存在 GitHub 上的代码文件,假如链接失效,你可以给我留言。
GCD.swift

为何 GCD

我在工作中经常使用 GCD,因为我感觉这样的代码更加直接了当,如果不是必要情况,我甚至不愿意使用 NSThread,NSOperationQueue,当然,有时候,这是一种非常折腾的癖好。
总之,这些都是多线程管理工具,如果你有兴趣,简书上有非常简单易懂的资料。稍微搜索一下即可。
至于好处,我只说一点。
这是 iOS 提供的最底层的多线程接口。

基础概念

--> 名词解释

  • Block 任务:具体要执行的工作内容
  • Queue 队列:一个用于放置 Block 的队伍,一个 Queue 中会有多个 Block。
  • Concurrent 并发:Queue 执行自己的 Block 的方式,并发表示会有多个 Block 同时运行。
  • Serial 串行:Queue 执行自己的 Block 的方式,串行表示所有 Block 会依次按顺序一个运行完才运行下一个。
  • sync 同步:不会开启新线程,堵塞当前线程来执行
  • async 异步:开启新线程,不堵塞当前线程
  • Thread 线程:程序中执行代码的通道,Queue 会把 Block 传递到这里来进行运行。
  • Process 进程:一个程序为一个进程,一个运行的 App 即是 iOS 系统当中运行的一个进程,一个进行可以有多个线程。


    我在练英语……所以别问我为什么不做中文。

--> GCD 变量

---> dispatch_queue_t:队列

    func TestGCD() {
        var queue: dispatch_queue_t
        // 创建串行队列
        queue = dispatch_queue_create("A Serial Queue", DISPATCH_QUEUE_SERIAL)
        // 创建并行队列
        queue = dispatch_queue_create("A Concurrent", DISPATCH_QUEUE_CONCURRENT)
        // 获取程序主队列
        queue = dispatch_get_main_queue()
        // 获取程序全局队列
        queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    }

---> dispatch_source_t: GCD 基础数据类型

官方解释:GCD provides a suite of dispatch sources—interfaces for monitoring (low-level system objects such as Unix descriptors, Mach ports, Unix signals, VFS nodes, and so forth) for activity. and submitting event handlers to dispatch queues when such activity occurs. When an event occurs, the dispatch source submits your task code asynchronously to the specified dispatch queue for processing.
翻译:GCD 提供一个系列的调度源接口用来监听活动(底层的系统对象:例如 Unix 描述符,Mach 端口,Unix 信号,VFS 节点等等)。并且在活动发生的时候发送事件句柄给调度的队列。当一个事件发生的时候,发送源派遣你的任务代码到指定的派遣队列中异步执行。

--> GCD 方法

---> 基础方法

----> dispatch_sync(queue: dispatch_queue_t, block: dispatch_block_t)

    func TestGCD() {
        // dispatch_sync(queue: dispatch_queue_t, block: dispatch_block_t)
        // 同步执行方法,会堵塞当前的线程,然后把该任务完成后才继续当前线程。
        // 注意:如果在 main_queue 中 dispatch_sync(queue: main_queue, block: {}) 会导致死锁
        
        print("Test Start in \(NSThread.currentThread()).")
        for i in 0 ..< 10 {
            dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
                print("Test \(i) Run in \(NSThread.currentThread()).")
            }
        }
        print("Test End in \(NSThread.currentThread()).")
    }

/* 运行结果
Test Start in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 0 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 1 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 2 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 3 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 4 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 5 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 6 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 7 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 8 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test 9 Run in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
Test End in <NSThread: 0x7fa490504cb0>{number = 1, name = main}.
*/

----> dispatch_async(queue: dispatch_queue_t, block: dispatch_block_t)

    func TestGCD() {
        // dispatch_async(queue: dispatch_queue_t, block: dispatch_block_t)
        // 异步执行方法,不会堵塞当前的线程,但是运行顺序将不可控。
        
        print("Test Start in \(NSThread.currentThread()).")
        for i in 0 ..< 10 {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
                print("Test \(i) Run in \(NSThread.currentThread()).")
            }
        }
        print("Test End in \(NSThread.currentThread()).")
    }
/* 运行结果
Test Start in <NSThread: 0x7fbe33c09bf0>{number = 1, name = main}.
Test End in <NSThread: 0x7fbe33c09bf0>{number = 1, name = main}.
Test 2 Run in <NSThread: 0x7fbe33e0b360>{number = 2, name = (null)}.
Test 0 Run in <NSThread: 0x7fbe33f018d0>{number = 3, name = (null)}.
Test 8 Run in <NSThread: 0x7fbe33e0b360>{number = 2, name = (null)}.
Test 1 Run in <NSThread: 0x7fbe33e0cf90>{number = 4, name = (null)}.
Test 3 Run in <NSThread: 0x7fbe33f05c90>{number = 5, name = (null)}.
Test 4 Run in <NSThread: 0x7fbe33f05880>{number = 6, name = (null)}.
Test 5 Run in <NSThread: 0x7fbe33d1dc60>{number = 7, name = (null)}.
Test 6 Run in <NSThread: 0x7fbe33d09660>{number = 8, name = (null)}.
Test 7 Run in <NSThread: 0x7fbe33f04cf0>{number = 9, name = (null)}.
Test 9 Run in <NSThread: 0x7fbe33f018d0>{number = 3, name = (null)}.
*/

---> dispatch_source_t 方法

----> dispatch_source_t dispatch_source_create( dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)

Creates a new dispatch source to monitor low-level system objects and automatically submit a handler block to a dispatch queue in response to events.
创建一个新的调度源来监听底层系统对象并在响应事件的时候自动提交处理块给派遣队列。

  • type:源处理事件类型
    • DISPATCH_SOURCE_TYPE_DATA_ADD:自定义的事件,变量增加
    • DISPATCH_SOURCE_TYPE_DATA_OR:自定义的事件,变量OR
    • DISPATCH_SOURCE_TYPE_MACH_SEND:MACH端口发送
    • DISPATCH_SOURCE_TYPE_MACH_RECV:MACH端口接收
    • DISPATCH_SOURCE_TYPE_PROC:进程监听,如进程的退出、创建一个或更多的子线程、进程收到UNIX信号
    • DISPATCH_SOURCE_TYPE_READ:IO操作,如对文件的操作、socket操作的读响应
    • DISPATCH_SOURCE_TYPE_SIGNAL:接收到UNIX信号时响应
    • DISPATCH_SOURCE_TYPE_TIMER:定时器
    • DISPATCH_SOURCE_TYPE_VNODE:文件状态监听,文件被删除、移动、重命名
    • DISPATCH_SOURCE_TYPE_WRITE:IO操作,如对文件的操作、socket操作的写响应
  • handle:句柄、索引或id,假如要监听进程,需要传入进程的ID
  • mask:可以理解为描述,提供更详细的描述,让它知道具体要监听什么
  • queue:自定义源需要的一个队列,用来处理所有的响应句柄(block)
// 在主队列中创建一个定时器
var timer: dispatch_source_t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue())
    // 创建 计时器 dispatch_source_t
    var source: dispatch_source_t? = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue())
    // 计数
    var sourceTimes = 10
    //
    func TestGCD() {
        // 设置 dispatch_source_t 为从现在开始每秒调用一次
        dispatch_source_set_timer(source!, dispatch_walltime(nil, 0), NSEC_PER_SEC, 0)
        // 设置调用时的处理
        dispatch_source_set_event_handler(source!) {
            // 打印当前线程,以及计数值,然后减1.
            print("\(self.sourceTimes): \(NSThread.currentThread())")
            self.sourceTimes -= 1
            // 如果计数结束则 释放 或 cancel, 都会导致停止,但是释放不会调用 cancel handler
            if self.sourceTimes <= 0 {
                //self.source = nil
                dispatch_source_cancel(self.source!)
            }
        }
        // 调用 cancel 的时候的处理
        dispatch_source_set_cancel_handler(source!) { 
            print("source Cancel: \(NSThread.currentThread())")
            self.source = nil
        }
        // 启动 source 监听
        dispatch_resume(source!)
    }

----> void dispatch_source_set_timer( dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway) 设置计时器时间

Sets a start time, interval, and leeway value for a timer source.
设置计时器源的起始时间、 间隔和回旋余地值。

----> dispatch_time_t dispatch_walltime( const struct timespec *when, int64_t delta)

Creates a dispatch_time_t using an absolute time according to the wall clock.
根据挂钟来创建一个绝对事件。

  • when:A struct timespec to add time to. If NULL is passed, then this function uses the result of gettimeofday.
  • delta:Nanoseconds to add.

----> dispatch_time_t dispatch_time( dispatch_time_t when, int64_t delta);

Parameters

Creates a dispatch_time_t relative to the default clock or modifies an existing dispatch_time_t.
根据默认的时钟创建一个 dispatch_time_t 或修改一个现有的 dispatch_time_t

  • when:The dispatch_time_t value to use as the basis for a new value. Pass DISPATCH_TIME_NOW to create a new time value relative to now.
  • delta:The number of nanoseconds to add to the time in the when parameter.

参考文献

我的 GitHub 空间:Myron Work Space

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

推荐阅读更多精彩内容

  • 程序中同步和异步是什么意思?有什么区别? 解释一:异步调用是通过使用单独的线程执行的。原始线程启动异步调用,异步调...
    风继续吹0阅读 1,024评论 1 2
  • 1. 并行和并发 简单来说,若说两个任务A和B并发执行,则表示任务A和任务B在同一时间段里被执行(更多的可能是二者...
    Z_Han阅读 636评论 0 8
  • GCD(Grand Central Dispatch)介绍 GCD是苹果为开发者提供的系统级别的线程管理api,开...
    江户川_乱步阅读 452评论 0 6
  • GCD笔记 总结一下多线程部分,最强大的无疑是GCD,那么先从这一块部分讲起. Dispatch Queue的种类...
    jins_1990阅读 754评论 0 1