结合GCD简单介绍下同步、异步、并行、串行、线程、多线程等概念

一、简单问答环节

1.什么是GCD

答:一种异步执行任务的技术

2.什么是线程

答:源代码通过编译器转换为CPU命令列,即二进制代码,一个CPU执行这些二进制代码的形式为一行一行执行,执行顺序是线性的(永远不会出现分叉),即为线程

3.什么是多线程

答:一个CPU执行多条这种线性的无分叉路径,即为多线程

4.一个CPU如何实现多线程

答:一个CPU同时只能做一件事,当CPU在多个线程间频繁的切换,由于速度很快,给人的感觉就是同时执行多个线程

5.多线程会产生哪些问题

答:1.更新相同的资源,导致数据不一致(数据竞争)
2.多个线程之间相互等待,谁也不往下执行(死锁)
3.多线程对内存消耗很大
还有很多问题,不一一列举

二、结合GCD的API讲解

1.Dispatch Queue(调度队列)

a.队列的种类

队列可以通俗的理解为坐火车检票,乘客排着队准备通过检票口,而每一个乘客相当于一个要执行的代码块(任务)
队列总体分为两种类型 Serial Dispatch QueueConcurrent Dispatch Queue,可以通俗的理解为一个检票口,和多个检票口(即串行队列和并行队列)
Serial Dispatch Queue 类型的队列每次只允许一个乘客通过,后面的乘客想通过检票口,必须要等前面的乘客先通过(每次只能执行一个代码块)
Concurrent Dispatch Queue 类型的队列相当于有多个检票口,可以同时允许很多乘客检票(即同时执行很多个代码块)

b.创建一个队列

队列的类型是dispatch_queue_t创建一个队列用dispatch_queue_create方法

// 创建一个Serial类型的队列
dispatch_queue_t   serial_queue = dispatch_queue_create("com.ms.serial", NULL);

// 创建一个concurrent类型的队列
    dispatch_queue_t     concurrent_queue = dispatch_queue_create("com.ms.concurrent", DISPATCH_QUEUE_CONCURRENT);

第一个参数是队列的名字,一般用程序ID逆序全域名的形式,这个参数在程序报错时候的返回信息中会体现,还是很有用的
第二个参数为生成指定类型队列的参数

注意

  • 每生成一个Serial类型的队列相当于生成并使用一个线程,所以不宜生成过多的Serial类型队列,这样内存会开销很大
  • 一个线程对数据来说是安全的,所以可以针对一个数据生成一个Serial类型的队列,例如更新一个文件,但是要避免针对一个文件生成多个Serial类型的队列,因为多线程的关系,会产生数据竞争
  • 当想要对一个数据进行并发操作,又要避免数据竞争问题的时候,用Concurrent类型的队列,系统会有效的管理这些线程,避免出现问题

c.系统自带的队列

GCD的API为我们准备了两个队列Main Dispatch QueueGlobal Dispatch Queue分别对应Serial类型的队列和Concurrent类型的队列

// 获取Main Dispatch Queue
dispatch_queue_t main_queue = dispatch_get_main_queue();
// 获取Global Dispatch Queue
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

Global Dispatch Queue有四种执行优先级,高优先级相当于会员的作用,可以让你插队,对应dispatch_get_global_queue方法的第一个参数,分别是

参数 优先级
DISPATCH_QUEUE_PRIORITY_HIGH 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台优先级

需要注意,优先级只是大致的判断,并不是绝对的

d.修改队列优先级

dispatch_queue_create方法创建的队列优先级与Global Dispatch Queue的默认优先级相同,即DISPATCH_QUEUE_PRIORITY_DEFAULT
想变更生成的队列的优先级的时候,用方法dispatch_set_target_queue
例子是将创建的concurrent_queue的优先级设置成DISPATCH_QUEUE_PRIORITY_BACKGROUND优先级

// 创建的队列,优先级是DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.ms.concurrent", DISPATCH_QUEUE_CONCURRENT);
// 获取的Global Dispatch Queue对列,注意优先级是 DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 修改concurrent_queue的优先级
dispatch_set_target_queue(concurrent_queue, global_queue);

注意

  • 不可以修改Main Dispatch Queue 和 Global Dispatch Queue的优先级

dispatch_set_target_queue的另一个作用是可以决定队列的执行层次,防止多个队列并行执行
假设有五个Serial Dispatch Queue,那么这五个Serial Dispatch Queue是并行执行的,如果将其中四个Serial Dispatch Queue设置为其中某一个Serial Dispatch Queue的优先级,则不会出现并行执行的情况

2.添加任务到队列

前面已经讲解了如何创建一个队列,下面介绍如何将任务添加到队列中
将任务添加到队列分为同步(sync)异步(async)
举个例子,你有车了,为了嘚瑟有车,要开车去送你的朋友去火车站坐火车,但是你可能有好几个朋友要去坐火车,但是这几个朋友之间相互不认识,分别在不同的地方,要分别送他们
同步(sync)就是你送一个朋友去火车站之后,必须确认你朋友经过检票口上了火车之后你才回去接其他的朋友,也不管其他的朋友是否着急
异步(async)就是你送去一个朋友之后,也没管它是否检票上车,就返回去接其他的朋友了

a. dispatch_sync()

void  dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

将任务同步添加到队列中
例子是将打印1的任务同步添加到serial_queue队列中

dispatch_queue_t serial_queue = dispatch_queue_create("com.ms.serial", NULL);
dispatch_sync(serial_queue, ^{
        // 要做的任务
        NSLog(@"1");
    });
  • 第一个参数是要添加的队列
  • 第二个参数是block,就是我们要添加的任务

b. dispatch_sync_f()

void  dispatch_sync_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);

作用和dispatch_sync()方法一样,不同点是这个方法可以将函数提交到队列中
例子中是将一个函数print1()添加到队列中,实现的也是打印1的任务

void print1(void *param){
    printf("%s", param);
}
char *str = "1";
dispatch_sync_f(serial_queue, str, print1);
  • 第一个参数是要添加的队列
  • 第二个参数是地址,这个参数是第三个参数(一个函数)要用的参数
  • 第三个参数是一个void (*dispatch_function_t)(void *_Nullable)类型的函数

c. dispatch_async()和dispatch_async_f()

类比上面的ab,就是同步和异步的区别

将任务添加到队列后,系统会对队列进行retain,当任务完成后队列被release

注意

  • dispatch_sync()和dispatch_async()中第二个参数是block,这个block中即使使用self也不会出现循环引用的情况,可以放心使用
  • dispatch_sync()别乱用,在串行队列中用dispatch_sync()将任务添加到当前串行队列,会发生死锁,因为block的执行需要等待前面的任务,也就是dispatch_sync执行完成,两者互相等待,在并行队列中就不会出现这种情况
  • 前面介绍过Main Dispatch Queue是串行队列,你懂的😏
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容