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

推荐阅读更多精彩内容