GCD是多线程编程中很常用的技术,同时,作为一项重要的知识点,在面试中也是“常来之客”,本文通过API进行GCD的归纳和总结
GCD之前
在苹果引入GCD技术之前,Cocoa框架中的NSObject类提供了performSelectorInBackground:withObject
和performSelectorOnMainThread
等来实现多线程编程,此外还有NSThread、NSOperation等,伴随GCD的诞生,NSOperation也进行重写,基于GCD实现
关于多线程编程
一个CPU一次只能执行一个命令,这样连续执行命令,相当于一条无分叉的路径,这样的路径就是“线程”。OS和iOS的核心XNU内核在进行操作系统事件处理的时候,会切换执行路径。每条执行路径的状态,会保存到每条路径专用的内存块中,便于下次执行时复原信息。这种来回切换的操作可以被称为“上下文切换”,也正是这种切换产生了多线程。
多线程需要注意的问题
- 数据不一致:例如,两个线程同时更新一个数据
- 死锁:两个线程互相等待对方执行结束
- 内存消耗:线程过多会消耗大量内存
...
GCD的API
dispatch_queue_t
分为两种
-
Serial Dispatch Queue
:串行队列-
系统提供的:
dispatch_get_main_queue
-
自定义创建:
dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL)
其中的name是该queue的名字
-
系统提供的:
-
Concurrent Dispatch Queue
:并行队列-
系统提供的:
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
其中第一个参数有四种,对应不同优先级DISPATCH_QUEUE_PRIORITY_BACKGROUND
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_HIGH
-
自定义创建:
dispatch_queue_create("name",DISPATCH_QUEUE_CONCURRENT)
-
系统提供的:
dispatch_set_target_queue
-
dispatch_queue_create
生成的dispatch_queue_t
的优先级都是默认优先级,如果想要改变优先级可以使用:
dispatch_queue_t queue1 = dispatch_queue_create("name", DISPATCH_QUQUE_SERIAL);
dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND);
dispatch_set_target_queue(queue1, queue2);
- 同时如果将多个串行队列的目标制定为同一个串行队列,那么本应该并行执行的各个串行队列只能串行地进行执行
dispatch_after
在指定时间之后执行处理,本质上是在指定时间之后将任务追加到队列中
dispatch_group_t
队列组可以用来实现:当所有处理结束之后,执行一个操作。正常来说使用串行队列是很容易实现的,但是如果是并行队列的话,想要实现这个操作,就需要借助队列组来实现
dispatch_group_notify
- 用于group中所有任务执行完之后追加一个操作执行
- 该方法不会阻塞当前线程
dispatch_group_wait
long result = dispatch_group_wait(group, time);
- 该函数会阻塞当前线程(即代码所处的线程)
- time的选择
- 可以是一个固定的时间,如果超时了,该函数就会返回
- 也可以是DISPATCH_TIME_FOREVER,表示一直等待,直到group中所有操作执行完毕,函数才返回。
- 返回值
- 0:代表group中的任务全部执行完了
- 非0:代表超时了
dispatch_barrier_async
一般来说,关于数据库的操作,使用串行队列能够避免数据异常的问题。但是实际上,读取操作之间是没有影响的,为了提高效率,读取操作通过是并行的,只需要保证写操作的时候,没有任何一个读取操作在进行即可。
dispatch_async(queue, block_reading1);
dispatch_async(queue, block_reading2);
dispatch_barrier_async(queue, block_writing);
dispatch_async(queue, block_reading3);
dispatch_async(queue, block_reading4);
以上代码中的写操作,会等待上面代码中的读操作(已经被添加到队列中的任务)执行完毕之后,开始执行,此时,写操作以下的代码中的读操作需要的等待写操作执行完之后才能添加到队列中执行
dispatch_async和dispatch_sync
-
dispatch_async
是异步执行,代表不会阻塞当前线程,会另外开辟线程执行任务 -
dispatch_sync
是同步执行,代表会阻塞当前线程,会在当前线程执行任务
dispatch_apply
该函数可以将block中的任务指定次数加入到队列中,并且会阻塞当前线程,直到所有的任务全部执行完
dispatch_suspend和dispatch_resume
-
dispatch_suspend
:挂起后,添加到队列中的任务,尚未执行的处理都会停止执行 -
dispatch_resume
:将会恢复以上操作
dispatch_semaphore_t
GCD中的信号量,用于控制并行执行,在GCD中,控制并行可以通串行队列和dispatch_barrier_async来做,而信号量semaphore可以更加精确地控制并行
-
dispatch_semaphore_create(number )
:number为信号量的初始量 -
dispatch_semaphore_wait(semaphore, time)
- 其中time和group中是一样的
- 返回值:
- 0:如果信号量大于等于1,返回0,同时将信息号量减去1
- 非0:超过指定时间(同时这时信号量为0)
-
dispatch_semaphore_signal(semaphore)
:将信号量+1
dispatch_once
用于单例处理,相关代码只会执行一次(即使在多核情况下,也是安全的)