乐虎游戏|乐虎国际登录|欢迎你

4.3 多线程进阶篇<中>(GCD),4.3gcd

日期:2020-04-20编辑作者:计算机资讯

Hello, World!Endtask11!----<NSThread: 0x1758fe10>{number = 2, name = }task33!-----<NSThread: 0x17673220>{number = 3, name = }task44!-----<NSThread: 0x1758fe10>{number = 2, name = }task22!----<NSThread: 0x17580d70>{number = 4, name = }

4.3 多线程进阶篇<中>(GCD),4.3gcd

更正:队列名称的作用的图中,箭头标注的有些问题,已修正     1.0 GCD简介   GCD概念 :(Grand Central Dispatch)可译为“伟大的中枢调度器”

  • 纯C语言,提供了非常多强大的函数

GCD 的优势: 

  • GCD是苹果公司为多核的并行运算提出的解决方案
  • GCD会自动利用更多的CPU内核(比如双核、四核)
  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

GCD的两个核心概念:  1) 任务:执行什么操作 2) 队列:用来存放任务   注意: (1)GCD存在于 libdispatch 这个库中,这个调度库包含了GCD的所有的东西,但任何IOS程序,默认就加载了这个库,在程序运行的过程中会动态的加载这个库,不需要我们手动导入。 (2)GCD是纯C语言的,因此我们在编写GCD相关代码的时候,面对的函数,而不是方法。 (3)GCD中的函数大多数都以dispatch开头。   2.0 GCD的使用 GCD的使用步骤: 步骤1 : 创建队列 步骤2 : 创建任务

  • 确定想做的事情

步骤3:把任务添加到队列

  • GCD会自动将队列中的任务取出,放到对应的线程中执行
  • 任务的取出遵循队列的FIFO原则:先进先出,后进后出

  2.1 GCD的使用 - 任务 任务的执行: 有两个函数来执行任务:

  • 同步
  • 异步

说明:把右边的参数(任务)dispatch_block_t block 提交给左边的参数(队列)dispatch_queue_t queue 进行执行。

 1 (1)任务的执行:同步
 2 
 3 //queue:队列    block:任务
 4 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
 5 
 6 (2)任务的执行:异步
 7 
 8 - 函数1
 9 
10 //queue:队列    block:任务
11 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
12 
13 - 函数2
14 
15 // 在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
16 dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

  【区别】同步 & 异步:同步和异步决定了要不要开启新的线程

  • 同步:只能在当前线程中执行任务,不具备开启新线程的能力
  • 异步:可以在新的线程中执行任务,具备开启新线程的能力 

【区别】并发 & 串行:并发和串行决定了任务的执行方式

  • 并发:允许多个任务并发(同时)执行
  • 串行:一个任务执行完毕后,再执行下一个任务

   2.2 GCD的使用 - 队列 队列分类: (1)并发队列(Concurrent Dispatch Queue)

  • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
  • 并发功能只有在异步(dispatch_async)函数下才有效

(2)串行队列(Serial Dispatch Queue)

  • 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

  release:

  • 凡是函数名种带有createcopynewretain等字眼, 都应该在不需要使用这个数据的时候进行release
  • GCD的数据类型在ARC环境下不需要再做release         dispatch_release(queue); // 非ARC需要释放手动创建的队列
  • CF(Core Foundation)的数据类型在ARC环境下还是需要再做release

  队列名称的作用:将来调试的时候,可以看得出任务是在哪个队列中执行的。 图片 1   2.2.1 创建队列 - 并发队列 并行队列中的任务是多个任务同时执行的  :

(1)如果异步任务前面有同步任务 就会先执行同步任务同步任务是按顺序执行的任务等他执行完了才会执行并行中的异步任务  (可以做到阻塞 控制任务的执行顺序)
(2)如果异步任务后面有同步任务  两个任务会并行(同时)执行

 

 1 方式1 - 手动创建并发队列:
 2 
 3 dispatch_queue_create(
 4     constchar *label,           // 队列名称
 5     dispatch_queue_attr_t attr  // 队列的类型
 6 );
 7 
 8 // 1.创建并发队列   DISPATCH_QUEUE_CONCURRENT (并发)
 9 dispatch_queue_t queue = dispatch_queue_create(“TD", DISPATCH_QUEUE_CONCURRENT);
10 
11 // 2.非ARC需要释放手动创建的队列
12 dispatch_release(queue);
13 
14 方式2 - 获取全局并发队列:(GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建)
15 
16 Xcode 7.2定义方式:
17 dispatch_get_global_queue(
18        long identifier,    // 队列的优先级
19        unsignedlong flags  // 此参数暂时无用,用0即可
20 );
21 
22 全局并发队列的优先级:(级别由高到低)
23 
24 #define DISPATCH_QUEUE_PRIORITY_HIGH 2               // 高
25 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0            // 默认(中)
26 #define DISPATCH_QUEUE_PRIORITY_LOW (-2)             // 低
27 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
28 
29 // 获取全局并发队列
30 dispatch_queue_t queue =
31       dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 

2.2.1【代码】同步 + 并发 在实际开发中,同步任务可以保证执行完成之后,才让后续的异步任务开始执行,用于控制任务之间的先后顺序,在后台线程中,处理“用户登录” 图片 2 1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 // Do any additional setup after loading the view, typically from a nib. 12 } 13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 15 [self syncConcurrent]; 16 } 17 18 #pragma mark - 同步函数 + 并发队列:不会开启新的线程,在当前线程执行任务(主线程),顺序执行,并发队列失去了并发的功能 19 - (void)syncConcurrent { 20 NSLog(@"同步并发 ----- begin"); 21 22 // 1.获得全局的并发队列 23 dispatch_queue_t queue = 24 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 25 26 // 2.将任务加入队列 27 dispatch_sync(queue, ^{ 28 NSLog(@"1-----%@", [NSThread currentThread]); 29 }); 30 dispatch_sync(queue, ^{ 31 NSLog(@"2-----%@", [NSThread currentThread]); 32 }); 33 dispatch_sync(queue, ^{ 34 NSLog(@"3-----%@", [NSThread currentThread]); 35 }); 36 37 NSLog(@"同步并发 ----- end"); 38 } 39 40 #pragma mark - 写法2 41 - (void)concurrentSync { 42 // 1. 创建并发队列 43 dispatch_queue_t conCurrentQueue = 44 dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT); 45 46 // 2. 创建任务 47 void (^task1)() = ^() { 48 NSLog(@"---task1---%@", [NSThread currentThread]); 49 }; 50 51 void (^task2)() = ^() { 52 NSLog(@"---task2---%@", [NSThread currentThread]); 53 }; 54 55 void (^task3)() = ^() { 56 NSLog(@"---task3---%@", [NSThread currentThread]); 57 }; 58 59 // 3. 将同步任务添加到并发队列中 60 dispatch_sync(conCurrentQueue, task1); 61 dispatch_sync(conCurrentQueue, task2); 62 dispatch_sync(conCurrentQueue, task3); 63 } 64 65 - (void)didReceiveMemoryWarning { 66 [super didReceiveMemoryWarning]; 67 // Dispose of any resources that can be recreated. 68 } 69 70 @end 71 72 打印结果: 73 74 2016-03-20 00:04:08.387 同步并发[1702:246074] 同步并发 ----- begin 75 2016-03-20 00:04:08.387 同步并发[1702:246074] 1-----<NSThread: 0x7fe963d07360>{number = 1, name = main} 76 2016-03-20 00:04:08.387 同步并发[1702:246074] 2-----<NSThread: 0x7fe963d07360>{number = 1, name = main} 77 2016-03-20 00:04:08.388 同步并发[1702:246074] 3-----<NSThread: 0x7fe963d07360>{number = 1, name = main} 78 2016-03-20 00:04:08.388 同步并发[1702:246074] 同步并发 ----- end 79 80 2016-03-20 00:05:07.968 同步并发[1724:247291] ---task1---<NSThread: 0x7f8e71400d20>{number = 1, name = main} 81 2016-03-20 00:05:07.969 同步并发[1724:247291] ---task2---<NSThread: 0x7f8e71400d20>{number = 1, name = main} 82 2016-03-20 00:05:07.969 同步并发[1724:247291] ---task3---<NSThread: 0x7f8e71400d20>{number = 1, name = main} View Code

 

2.2.1【代码】异步 + 并发 图片 3 1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 // Do any additional setup after loading the view, typically from a nib. 12 } 13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 15 [self asyncConcurrent]; 16 } 17 18 #pragma mark - 异步函数 + 并发队列:可以同时开启多条线程,在当前线程执行任务(主线程),无序执行(按照任务添加到队列中的顺序被调度),线程条数具体由`可调度线程池/底层线程池`来决定 19 - (void)asyncConcurrent { 20 NSLog(@"异步并发 ----- begin"); 21 22 // 1.获得全局的并发队列 23 dispatch_queue_t queue = 24 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 25 26 // 2.将任务加入队列 27 dispatch_sync(queue, ^{ 28 NSLog(@"1-----%@", [NSThread currentThread]); 29 }); 30 dispatch_sync(queue, ^{ 31 NSLog(@"2-----%@", [NSThread currentThread]); 32 }); 33 dispatch_sync(queue, ^{ 34 NSLog(@"3-----%@", [NSThread currentThread]); 35 }); 36 37 NSLog(@"异步并发 ----- begin"); 38 } 39 40 #pragma mark - 写法2 41 - (void)concurrentAsync { 42 // 1.创建并发队列 43 dispatch_queue_t conCurrentQueue = 44 dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT); 45 46 // 2. 创建任务 47 void (^task1)() = ^() { 48 NSLog(@"---task1---%@", [NSThread currentThread]); 49 }; 50 51 void (^task2)() = ^() { 52 NSLog(@"---task2---%@", [NSThread currentThread]); 53 }; 54 55 void (^task3)() = ^() { 56 NSLog(@"---task3---%@", [NSThread currentThread]); 57 }; 58 59 // 3. 将异步任务添加到并发队列中 60 dispatch_async(conCurrentQueue, task1); 61 dispatch_async(conCurrentQueue, task2); 62 dispatch_async(conCurrentQueue, task3); 63 } 64 65 - (void)didReceiveMemoryWarning { 66 [super didReceiveMemoryWarning]; 67 // Dispose of any resources that can be recreated. 68 } 69 70 @end 71 72 打印结果: 73 74 2016-03-20 00:11:16.307 异步并发[1879:254274] 异步并发 ----- begin 75 2016-03-20 00:11:16.308 异步并发[1879:254274] 1-----<NSThread: 0x7fd598d02490>{number = 1, name = main} 76 2016-03-20 00:11:16.308 异步并发[1879:254274] 2-----<NSThread: 0x7fd598d02490>{number = 1, name = main} 77 2016-03-20 00:11:16.308 异步并发[1879:254274] 3-----<NSThread: 0x7fd598d02490>{number = 1, name = main} 78 2016-03-20 00:11:16.308 异步并发[1879:254274] 异步并发 ----- begin 79 80 2016-03-20 00:18:18.557 异步并发[1945:260502] ---task2---<NSThread: 0x7fbf68d927b0>{number = 3, name = (null)} 81 2016-03-20 00:18:18.557 异步并发[1945:260628] ---task3---<NSThread: 0x7fbf68e24570>{number = 4, name = (null)} 82 2016-03-20 00:18:18.557 异步并发[1945:260503] ---task1---<NSThread: 0x7fbf68f15ae0>{number = 2, name = (null)} View Code

 

2.2.2 创建队列 - 串行队列

 1 手动创建串行队列:
 2 
 3 dispatch_queue_create(
 4     constchar *label,           // 队列名称
 5     dispatch_queue_attr_t attr  // 队列的类型
 6 );
 7 
 8 //1.创建串行队列
 9 
10 //方式1:DISPATCH_QUEUE_SERIAL (串行)
11 dispatch_queue_t queue = dispatch_queue_create(“TD", DISPATCH_QUEUE_SERIAL);
12 //方式2:传 NULL
13 dispatch_queue_t queue = dispatch_queue_create(“TD", NULL);
14 
15 // 2.非ARC需要释放手动创建的队列
16 dispatch_release(queue);
  • 串行队列中的任务都是按顺序执行,谁在前就先执行谁
  • 主线程和子线程平等,一样谁在前选执行谁
  • 执行完一个才会执行下一个任务

   2.2.2【代码】同步 + 串行

  • 串行队列中的任务都是按顺序执行,谁在前就先执行谁
  • 主线程和子线程平等,一样谁在前选执行谁
  • 执行完一个才会执行下一个任务

图片 4 1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 // Do any additional setup after loading the view, typically from a nib. 12 } 13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 15 [self syncSerial]; 16 } 17 18 #pragma mark - 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行) 19 - (void)syncSerial { 20 NSLog(@"同步串行 ----- begin"); 21 22 // 1.创建串行队列 23 dispatch_queue_t queue = dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL); 24 25 // 2.将任务加入队列 26 dispatch_sync(queue, ^{ 27 NSLog(@"1-----%@", [NSThread currentThread]); 28 }); 29 dispatch_sync(queue, ^{ 30 NSLog(@"2-----%@", [NSThread currentThread]); 31 }); 32 dispatch_sync(queue, ^{ 33 NSLog(@"3-----%@", [NSThread currentThread]); 34 }); 35 36 NSLog(@"同步串行 ----- end"); 37 } 38 39 #pragma mark - 写法2 40 - (void)serialSyncDemo { 41 // 1.创建队列 42 dispatch_queue_t serialQueue = 43 dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL); 44 45 // 2.创建任务 46 void (^task1)() = ^() { 47 NSLog(@"task1---%@", [NSThread currentThread]); 48 }; 49 50 void (^task2)() = ^() { 51 NSLog(@"task2---%@", [NSThread currentThread]); 52 }; 53 54 void (^task3)() = ^() { 55 NSLog(@"task3---%@", [NSThread currentThread]); 56 }; 57 58 // 3.将同步任务,添加到串行队列 59 dispatch_sync(serialQueue, task3); 60 dispatch_sync(serialQueue, task1); 61 dispatch_sync(serialQueue, task2); 62 } 63 64 - (void)didReceiveMemoryWarning { 65 [super didReceiveMemoryWarning]; 66 // Dispose of any resources that can be recreated. 67 } 68 69 @end 70 71 打印结果: 72 73 2016-03-20 00:38:13.648 同步串行[2145:276628] 同步串行 ----- begin 74 2016-03-20 00:38:13.649 同步串行[2145:276628] 1-----<NSThread: 0x7fab52f04910>{number = 1, name = main} 75 2016-03-20 00:38:13.649 同步串行[2145:276628] 2-----<NSThread: 0x7fab52f04910>{number = 1, name = main} 76 2016-03-20 00:38:13.649 同步串行[2145:276628] 3-----<NSThread: 0x7fab52f04910>{number = 1, name = main} 77 2016-03-20 00:38:13.649 同步串行[2145:276628] 同步串行 ----- end 78 79 2016-03-20 00:47:53.272 同步串行[2248:284920] task1---<NSThread: 0x7fd910c05150>{number = 1, name = main} 80 2016-03-20 00:47:53.273 同步串行[2248:284920] task2---<NSThread: 0x7fd910c05150>{number = 1, name = main} 81 2016-03-20 00:47:53.273 同步串行[2248:284920] task3---<NSThread: 0x7fd910c05150>{number = 1, name = main} View Code

 

2.2.2【代码】异步 + 串行

  • 串行队列中的任务都是按顺序执行,谁在前就先执行谁
  • 主线程和子线程平等,一样谁在前选执行谁
  • 执行完一个才会执行下一个任务
串行队列,异步任务,在多线程中,是斯坦福大学最推荐的一种多线程方式!
  • 优点:将任务放在其他线程中工作,每个任务顺序执行,便于调试
  • 缺点:并发能力不强,最多只能使用一条线程!

  图片 5 1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 // Do any additional setup after loading the view, typically from a nib. 12 } 13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 15 [self asyncSerial]; 16 } 17 18 #pragma mark - 异步函数 + 串行队列:会开启新的线程,在子线程执行任务,任务是串行的(顺序执行),只开一条线程 19 - (void)asyncSerial { 20 NSLog(@"异步串行 ----- begin"); 21 NSLog(@"主线程 ----- %@", [NSThread mainThread]); 22 23 // 1.创建串行队列 24 //写法1: 25 dispatch_queue_t queue = dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL); 26 //写法2: 27 dispatch_queue_t queue = dispatch_queue_create("TD", NULL); 28 29 // 2.将任务加入队列 30 dispatch_async(queue, ^{ 31 NSLog(@"1-----%@", [NSThread currentThread]); 32 }); 33 dispatch_async(queue, ^{ 34 NSLog(@"2-----%@", [NSThread currentThread]); 35 }); 36 dispatch_async(queue, ^{ 37 NSLog(@"3-----%@", [NSThread currentThread]); 38 }); 39 40 NSLog(@"异步串行 ----- end"); 41 } 42 43 #pragma mark - 写法2 44 - (void)serialAsyncDemo { 45 // 1.创建队列 46 dispatch_queue_t serialQueue = 47 dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL); 48 49 // 2.创建任务 50 void (^task1)() = ^() { 51 NSLog(@"task1---%@", [NSThread currentThread]); 52 }; 53 54 void (^task2)() = ^() { 55 NSLog(@"task2---%@", [NSThread currentThread]); 56 }; 57 58 void (^task3)() = ^() { 59 NSLog(@"task3---%@", [NSThread currentThread]); 60 }; 61 62 // 3.将异步任务添加到串行队列 63 dispatch_async(serialQueue, task1); 64 dispatch_async(serialQueue, task2); 65 dispatch_async(serialQueue, task3); 66 } 67 68 - (void)didReceiveMemoryWarning { 69 [super didReceiveMemoryWarning]; 70 // Dispose of any resources that can be recreated. 71 } 72 73 @end 74 75 打印结果: 76 77 2016-03-20 00:59:38.392 异步串行[2486:297960] 异步串行 ----- begin 78 2016-03-20 00:59:38.393 异步串行[2486:297960] 主线程 ----- <NSThread: 0x7ff271701ba0>{number = 1, name = main} 79 2016-03-20 00:59:38.393 异步串行[2486:297960] 异步串行 ----- end 80 2016-03-20 00:59:38.393 异步串行[2486:298011] 1-----<NSThread: 0x7ff2717abb30>{number = 2, name = (null)} 81 2016-03-20 00:59:38.394 异步串行[2486:298011] 2-----<NSThread: 0x7ff2717abb30>{number = 2, name = (null)} 82 2016-03-20 00:59:38.394 异步串行[2486:298011] 3-----<NSThread: 0x7ff2717abb30>{number = 2, name = (null)} 83 84 2016-03-20 01:02:21.844 异步串行[2529:301017] task1---<NSThread: 0x7fddb9405f40>{number = 2, name = (null)} 85 2016-03-20 01:02:21.845 异步串行[2529:301017] task2---<NSThread: 0x7fddb9405f40>{number = 2, name = (null)} 86 2016-03-20 01:02:21.845 异步串行[2529:301017] task3---<NSThread: 0x7fddb9405f40>{number = 2, name = (null)} View Code

 

2.2.3 创建队列 - 主队列(特殊的串行队列)

主队列(跟主线程相关联的队列):

  • 主队列是GCD自带的一种特殊的串行队列
  • 放在主队列中的任务,都会放到主线程中执行
  • 如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程。
  • 主队列中不能用同步任务,无论是在异步任务前还是后都会死锁
1 获取主队列:
2 
3 dispatch_get_main_queue(void);
4 
5 dispatch_queue_t queue = dispatch_get_main_queue();

 

2.2.3【代码】同步 + 主队列

主队列中不能用同步任务,无论是在异步任务前还是后都会死锁

图片 6 1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 // Do any additional setup after loading the view, typically from a nib. 12 } 13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 15 [self syncMain]; 16 } 17 18 #pragma mark - 同步函数 + 主队列:不会开启新的线程,会出现"死等",可能导致`主线程`卡死 19 - (void)syncMain { 20 NSLog(@"同步主队列 ----- begin"); 21 22 // 1.获得主队列 23 dispatch_queue_t queue = dispatch_get_main_queue(); 24 25 // 2.将任务加入队列 26 dispatch_sync(queue, ^{ 27 NSLog(@"1-----%@", [NSThread currentThread]); 28 }); 29 dispatch_sync(queue, ^{ 30 NSLog(@"2-----%@", [NSThread currentThread]); 31 }); 32 dispatch_sync(queue, ^{ 33 NSLog(@"3-----%@", [NSThread currentThread]); 34 }); 35 36 NSLog(@"同步主队列 ----- end"); 37 } 38 39 - (void)mainQueueSync { 40 NSLog(@"同步主队列 ----- begin"); 41 42 // 1.获取主队列 43 dispatch_queue_t mainQueue = dispatch_get_main_queue(); 44 45 // 2.创建队列 46 void (^task1)() = ^() { 47 NSLog(@"---task1---%@", [NSThread currentThread]); 48 }; 49 50 void (^task2)() = ^() { 51 NSLog(@"---task2---%@", [NSThread currentThread]); 52 }; 53 54 void (^task3)() = ^() { 55 NSLog(@"---task3---%@", [NSThread currentThread]); 56 }; 57 58 // 3.将同步任务添加到并发队列中 59 dispatch_sync(mainQueue, task1); 60 dispatch_sync(mainQueue, task2); 61 dispatch_sync(mainQueue, task3); 62 63 NSLog(@"同步主队列 ----- end"); 64 } 65 66 - (void)didReceiveMemoryWarning { 67 [super didReceiveMemoryWarning]; 68 // Dispose of any resources that can be recreated. 69 } 70 71 @end 72 73 打印结果: 74 75 2016-03-20 01:23:55.594 同步主队列[3286:329220] 同步主队列 ----- begin View Code

 

2.3【代码】异步 + 主队列

 

图片 7 1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 // Do any additional setup after loading the view, typically from a nib. 12 } 13 14 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 15 [self asyncMain]; 16 } 17 18 #pragma mark - 异步函数 + 主队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行),只开一条线程(适合处理 UI 或者是 UI事件) 19 - (void)asyncMain { 20 NSLog(@"异步主队列 ----- begin"); 21 22 // 1.获得主队列 23 dispatch_queue_t queue = dispatch_get_main_queue(); 24 25 // 2.将任务加入队列 26 dispatch_async(queue, ^{ 27 NSLog(@"1-----%@", [NSThread currentThread]); 28 }); 29 dispatch_async(queue, ^{ 30 NSLog(@"2-----%@", [NSThread currentThread]); 31 }); 32 dispatch_async(queue, ^{ 33 NSLog(@"3-----%@", [NSThread currentThread]); 34 }); 35 36 NSLog(@"异步主队列 ----- end"); 37 } 38 39 #pragma mark - 写法2 40 - (void)mainQueueAsync { 41 NSLog(@"异步主队列 ----- begin"); 42 43 // 1.获取主队列 44 dispatch_queue_t mainQueue = dispatch_get_main_queue(); 45 46 // 2.创建任务 47 void (^task1)() = ^() { 48 NSLog(@"---async task1---%@", [NSThread currentThread]); 49 }; 50 51 void (^task2)() = ^() { 52 NSLog(@"---async task2---%@", [NSThread currentThread]); 53 }; 54 55 void (^task3)() = ^() { 56 NSLog(@"---async task3---%@", [NSThread currentThread]); 57 }; 58 59 // 3.将异步任务添加到主队列中 60 dispatch_async(mainQueue, task1); 61 dispatch_async(mainQueue, task2); 62 dispatch_async(mainQueue, task3); 63 64 NSLog(@"异步主队列 ----- end"); 65 } 66 67 - (void)didReceiveMemoryWarning { 68 [super didReceiveMemoryWarning]; 69 // Dispose of any resources that can be recreated. 70 } 71 72 @end 73 74 打印结果: 75 76 2016-03-20 01:15:47.663 异步主队列[2949:318506] 异步主队列 ----- begin 77 2016-03-20 01:15:47.663 异步主队列[2949:318506] 异步主队列 ----- end 78 2016-03-20 01:15:47.664 异步主队列[2949:318506] 1-----<NSThread: 0x7feec9c082e0>{number = 1, name = main} 79 2016-03-20 01:15:47.664 异步主队列[2949:318506] 2-----<NSThread: 0x7feec9c082e0>{number = 1, name = main} 80 2016-03-20 01:15:47.664 异步主队列[2949:318506] 3-----<NSThread: 0x7feec9c082e0>{number = 1, name = main} 81 82 2016-03-20 01:16:15.690 异步主队列[2970:319219] 异步主队列 ----- begin 83 2016-03-20 01:16:15.691 异步主队列[2970:319219] 异步主队列 ----- end 84 2016-03-20 01:16:15.691 异步主队列[2970:319219] ---async task1---<NSThread: 0x7f9de1c074e0>{number = 1, name = main} 85 2016-03-20 01:16:15.691 异步主队列[2970:319219] ---async task2---<NSThread: 0x7f9de1c074e0>{number = 1, name = main} 86 2016-03-20 01:16:15.692 异步主队列[2970:319219] ---async task3---<NSThread: 0x7f9de1c074e0>{number = 1, name = main} View Code

 

 

 

2.2.4 总结

GCD 队列类型的创建方式:

  • 并发队列:手动创建、全局
  • 串行队列:手动创建、主队列            同步函数 + 并发队列:不会开启新的线程,在当前线程执行任务(主线程),顺序执行,并发队列失去了并发的功能   异步函数 + 并发队列:可以同时开启多条线程,在当前线程执行任务(主线程),无序执行(按照任务添加到队列中的顺序被调度),线程条数具体由`可调度线程池/底层线程池`来决定   并行队列中的任务是多个任务同时执行的  :

    (1)如果异步任务前面有同步任务 就会先执行同步任务同步任务是按顺序执行的任务等他执行完了才会执行并行中的异步任务  (可以做到阻塞 控制任务的执行顺序)
    (2)如果异步任务后面有同步任务  两个任务会并行(同时)执行

     


    (2) 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行)   异步函数 + 串行队列:会开启新的线程,在子线程执行任务,任务是串行的(顺序执行),只开一条线程  


    (3)  异步函数 + 主队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行),只开一条线程(适合处理 UI 或者是 UI事件)   同步函数 + 主队列:不会开启新的线程,会出现"死等",可能导致`主线程`卡死   主队列中不能用同步任务,无论是在异步任务前还是后都会死锁   


   **3.0 线程间的通信**
![](http://www.bkjia.com/uploads/allimg/160326/1614364921-1.gif) 1
#import "ViewController.h" 2 3 @interface ViewController () 4
@property(weak, nonatomic) IBOutlet UIImageView *imageView; 5 @end
6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10
[super viewDidLoad]; 11 // Do any additional setup after loading
the view, typically from a nib. 12 } 13 14 -
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
15 16 // 全局的异步并发 17 dispatch_async( 18
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{ 19 20 // 图片的网络路径 21 NSURL *url = [NSURL 22
URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"];
23 24 // 加载图片 25 NSData *data = [NSData
dataWithContentsOfURL:url]; 26 27 // 生成图片 28 UIImage *image =
[UIImage imageWithData:data]; 29 30 // 回到主线程,执行 UI
刷新操作 31 dispatch_async(dispatch_get_main_queue(), ^{ 32
self.imageView.image = image; 33 }); 34 }); 35 } 36 37 -
(void)didReceiveMemoryWarning { 38 [super
didReceiveMemoryWarning]; 39 // Dispose of any resources that can
be recreated. 40 } 41 42 @end 图片下载示例

 

**4.1 其他用法 - barrier函数**

![](http://www.bkjia.com/uploads/allimg/160326/1614364921-1.gif) 1
#import "ViewController.h" 2 3 @interface ViewController () 4 @end
5 6 @implementation ViewController 7 8 - (void)viewDidLoad { 9
[super viewDidLoad]; 10 // Do any additional setup after loading
the view, typically from a nib. 11 } 12 13 -
(void)touchesBegan:(NSSet&lt;UITouch *&gt; *)touches
withEvent:(UIEvent *)event { 14 15 //
这里使用全局并发队列的方式会导致 dispatch_barrier_async 功能失效
16 dispatch_queue_t queue = 17 dispatch_queue_create("TD",
DISPATCH_QUEUE_CONCURRENT); 18 19 dispatch_async(queue, ^{ 20
NSLog(@"----1-----%@", [NSThread currentThread]); 21 }); 22
dispatch_async(queue, ^{ 23 NSLog(@"----2-----%@", [NSThread
currentThread]); 24 }); 25 26 dispatch_barrier_async(queue, ^{ 27
NSLog(@"----barrier-----%@", [NSThread currentThread]); 28 }); 29
30 dispatch_async(queue, ^{ 31 NSLog(@"----3-----%@", [NSThread
currentThread]); 32 }); 33 dispatch_async(queue, ^{ 34
NSLog(@"----4-----%@", [NSThread currentThread]); 35 }); 36 } 37
38 - (void)didReceiveMemoryWarning { 39 [super
didReceiveMemoryWarning]; 40 // Dispose of any resources that can
be recreated. 41 } 42 43 @end 44 45 打印结果: 46 47 2016-03-21
15:42:16.214 其他用法 - barrier函数[1419:94670]
----2-----&lt;NSThread: 0x7fc4a0c2c5d0&gt;{number = 3, name =
(null)} 48 2016-03-21 15:42:16.214 其他用法 -
barrier函数[1419:94732] ----1-----&lt;NSThread:
0x7fc4a0f0cb20&gt;{number = 2, name = (null)} 49 2016-03-21
15:42:16.214 其他用法 - barrier函数[1419:94732]
----barrier-----&lt;NSThread: 0x7fc4a0f0cb20&gt;{number = 2, name =
(null)} 50 2016-03-21 15:42:16.215 其他用法 -
barrier函数[1419:94732] ----3-----&lt;NSThread:
0x7fc4a0f0cb20&gt;{number = 2, name = (null)} 51 2016-03-21
15:42:16.215 其他用法 - barrier函数[1419:94670]
----4-----&lt;NSThread: 0x7fc4a0c2c5d0&gt;{number = 3, name =
(null)} View Code

 

 

 

**4.2 其他用法 - 延迟执行**

     1 方法1:调用NSObject的方法
     2 
     3 // 该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程
     4 // 2秒后再调用self的run方法
     5 [selfperformSelector:@selector(run) withObject:nilafterDelay:2.0];
     6 
     7 方法2:使用GCD函数
     8 
     9 // 这里是在主线程执行,如果想要在子线程执行,选择相应的队列
    10 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    11         // 2秒后异步执行这里的代码...
    12     });
    13 
    14 方法3:使用NSTimer
    15 
    16 [NSTimer scheduledTimerWithTimeInterval:2.0
    17                                  target:self
    18                                selector:@selector(run)
    19                                userInfo:nil
    20                                 repeats:NO];

 

![](http://www.bkjia.com/uploads/allimg/160326/1614364921-1.gif) 1
#import "ViewController.h" 2 3 @interface ViewController () 4 5
@end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10
[super viewDidLoad]; 11 // Do any additional setup after loading
the view, typically from a nib. 12 } 13 14 -
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
15 16 [self delay1]; 17 } 18 19 #pragma mark -
方法1:调用NSObject的方法 20 - (void)delay1 { 21
NSLog(@"touchesBegan-----"); 22 23 // 2秒后再调用self的run方法 24
[self performSelector:@selector(run) withObject:nil
afterDelay:2.0]; 25 } 26 27 #pragma mark - 方法2:使用 GCD 函数
28 - (void)delay2 { 29 NSLog(@"touchesBegan-----"); 30 31 //
这里是在主线程执行,如果想要在子线程执行,选择相应的队列 32
dispatch_after( 33 dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(2.0 * NSEC_PER_SEC)), 34 dispatch_get_main_queue(),
^{ 35 NSLog(@"run-----"); 36 }); 37 } 38 39 #pragma mark -
方法3:使用NSTimer定时器 40 - (void)delay3 { 41
NSLog(@"touchesBegan-----"); 42 43 [NSTimer
scheduledTimerWithTimeInterval:2.0 44 target:self 45
selector:@selector(run) 46 userInfo:nil 47 repeats:NO]; 48 } 49
50 - (void)run { 51 NSLog(@"run-----"); 52 } 53 54 -
(void)didReceiveMemoryWarning { 55 [super
didReceiveMemoryWarning]; 56 // Dispose of any resources that can
be recreated. 57 } 58 59 @end 60 61 打印结果: 62 63 2016-03-21
16:01:56.384 其他用法 - 延迟执行[1651:114465] touchesBegan----- 64
2016-03-21 16:01:58.385 其他用法 - 延迟执行[1651:114465] run-----
View Code

 

 

 

**4.3 其他用法 - 一次性代码**

    1 使用dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,适合做资源的加载
    2 
    3 static dispatch_once_t onceToken;
    4   dispatch_once(&onceToken, ^{
    5      // 只执行1次的代码(这里面默认是线程安全的)
    6   });
    7 }

 

**4.4 其他用法 - 快速迭代**

    1 使用dispatch_apply函数能进行快速迭代遍历:
    2 
    3 dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t queue#>, ^(size_t) {
    4     //  代码
    5 });
    6 
    7 dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
    8     // 执行10次代码,index顺序不确定
    9 }); 

 

![](http://www.bkjia.com/uploads/allimg/160326/1614364921-1.gif) 1
#import "ViewController.h" 2 3 @interface ViewController () 4 5
@end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10
[super viewDidLoad]; 11 // Do any additional setup after loading
the view, typically from a nib. 12 } 13 - (void)touchesBegan:(NSSet
*)touches withEvent:(UIEvent *)event { 14 [self apply]; 15 } 16
17 #pragma mark - 文件剪切方法1:快速迭代 18 - (void)apply { 19 20
dispatch_queue_t queue = 21
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
22 23 NSString *from = @"/Users/TD/Desktop/From"; 24 NSString *to
= @"/Users/TD/Desktop/To"; 25 26 NSFileManager *mgr =
[NSFileManager defaultManager]; 27 NSArray *subpaths = [mgr
subpathsAtPath:from]; 28 29 dispatch_apply(subpaths.count, queue,
^(size_t index) { 30 31 NSString *subpath = subpaths[index]; 32
NSString *fromFullpath = [from
stringByAppendingPathComponent:subpath]; 33 NSString *toFullpath =
[to stringByAppendingPathComponent:subpath]; 34 35 // 剪切 36
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil]; 37
38 NSLog(@"%@---%@", [NSThread currentThread], subpath); 39 }); 40
} 41 42 #pragma mark - 文件剪切方法2:传统方式 43 - (void)moveFile
{ 44 NSString *from = @"/Users/TD/Desktop/From"; 45 NSString *to =
@"/Users/TD/Desktop/To"; 46 47 NSFileManager *mgr = [NSFileManager
defaultManager]; 48 49
//获取文件夹下的所有文件路径,包括子文件夹下的文件路径 50 NSArray
*subpaths = [mgr subpathsAtPath:from]; 51 52 for (NSString
*subpath in subpaths) { 53 54 //全路径 55 NSString *fromFullpath =
[from stringByAppendingPathComponent:subpath]; 56 NSString
*toFullpath = [to stringByAppendingPathComponent:subpath]; 57 58
dispatch_async( 59
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{ 60 // 剪切 61 [mgr moveItemAtPath:fromFullpath toPath:toFullpath
error:nil]; 62 }); 63 } 64 } 65 66 - (void)didReceiveMemoryWarning
{ 67 [super didReceiveMemoryWarning]; 68 // Dispose of any
resources that can be recreated. 69 } 70 71 @end 文件剪切示例

 

 

 

**4.5 其他用法 - 队列组/调度组**

有这么一种需求: 首先:分别异步执行2个耗时的操作
其次:等2个异步操作都执行完毕后,再回到主线程执行操作   
解决办法:用队列组,也叫做调度组  

     1 // 创建一个队列组
     2 dispatch_group_t group = dispatch_group_create();
     3 
     4 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     5    // 执行1个耗时的异步操作
     6 });
     7 
     8 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     9    // 执行1个耗时的异步操作
    10 });
    11 
    12 dispatch_group_notify(group, dispatch_get_main_queue(),
    13 ^{
    14    // 等前面的异步操作都执行完毕后,回到主线程...
    15 });

![](http://www.bkjia.com/uploads/allimg/160326/1614364921-1.gif) 1
#import "ViewController.h" 2 3 @interface ViewController () 4
@property(weak, nonatomic) IBOutlet UIImageView *imageView; 5
@property(nonatomic, strong) UIImage *image1; //图片1 6
@property(nonatomic, strong) UIImage *image2; //图片2 7 @end 8 9
@implementation ViewController 10 11 - (void)viewDidLoad { 12
[super viewDidLoad]; 13 // Do any additional setup after loading
the view, typically from a nib. 14 } 15 16 -
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
17 18 dispatch_queue_t queue = 19
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
20 21 // 创建一个队列组 22 dispatch_group_t group =
dispatch_group_create(); 23 24 // 1.下载图片1 25
dispatch_group_async(group, queue, ^{ 26 // 图片的网络路径 27
NSURL *url = [NSURL
URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
28 // 加载图片 29 NSData *data = [NSData
dataWithContentsOfURL:url]; 30 // 生成图片 31 self.image1 =
[UIImage imageWithData:data]; 32 }); 33 34 // 2.下载图片2 35
dispatch_group_async(group, queue, ^{ 36 // 图片的网络路径 37
NSURL *url = [NSURL URLWithString:
@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
38 // 加载图片 39 NSData *data = [NSData
dataWithContentsOfURL:url]; 40 // 生成图片 41 self.image2 =
[UIImage imageWithData:data]; 42 }); 43 44 //
3.将图片1、图片2合成一张新的图片(也可以直接在此处回到主线程,只不过是因为绘制图片比较耗时,没有放在主线程而已)
45 dispatch_group_notify(group, queue, ^{ 46 47 //
开启新的图形上下文 48 UIGraphicsBeginImageContext(CGSizeMake(100,
100)); 49 50 // 绘制图片 51 [self.image1 drawInRect:CGRectMake(0,
0, 50, 100)]; 52 [self.image2 drawInRect:CGRectMake(50, 0, 50,
100)]; 53 54 // 取得上下文中的图片 55 UIImage *image =
UIGraphicsGetImageFromCurrentImageContext(); 56 57 // 结束上下文 58
UIGraphicsEndImageContext(); 59 60 // 回到主线程显示图片 61
dispatch_async(dispatch_get_main_queue(), ^{ 62 //
4.将新图片显示出来 63 self.imageView.image = image; 64 }); 65 }); 66
} 67 68 - (void)didReceiveMemoryWarning { 69 [super
didReceiveMemoryWarning]; 70 // Dispose of any resources that can
be recreated. 71 } 72 73 @end 图片下载后合成示例

大致概况如下:

 **5.0 GCD 的定时器事件**

实际上 RunLoop 底层也会用到 GCD 的东西,比如 RunLoop 是用
dispatch_source_t 实现的 Timer。但同时 GCD 提供的某些接口也用到了
RunLoop, 例如 dispatch_async()。   当调用
dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch
会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个
block,并在回调
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()
里执行这个 block。但这个逻辑仅限于 dispatch 到主线程,dispatch
到其他线程仍然是由 libDispatch 处理的。    【区别】NSTimer & GCD
的定时器:

-   CFRunLoopTimerRef 基本上说的就是 NSTimer,它受 RunLoop 的 Mode
    影响
-   GCD 的定时器不受 RunLoop 的 Mode 影响

 

代码示例:当滚动文字的时候,是不会影响 GCD的定时器的

![](http://www.bkjia.com/uploads/allimg/160326/1614364921-1.gif) 1
#import "ViewController.h" 2 3 @interface ViewController () 4 //
定时器
(这里不用带*,因为dispatch_source_t就是个类,内部已经包含了*) 5
@property(nonatomic, strong) dispatch_source_t timer; 6 @end 7 8
@implementation ViewController 9 10 - (void)viewDidLoad { 11 [super
viewDidLoad]; 12 } 13 14 int count = 0; 15 -
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
16 17 // 1、获得队列 18 // dispatch_queue_t queue =
dispatch_get_global_queue(0, 0); 19 dispatch_queue_t queue =
dispatch_get_main_queue(); 20 21 // 2、创建一个定时器
(dispatch_source_t本质还是个OC对象) 22 self.timer =
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
queue); 23 24 //
3、设置定时器的各种属性(几时开始任务,每隔多长时间执行一次) 25 26
// 触发时间(何时开始执行第一个任务) 27 // 比当前时间晚1秒 28
dispatch_time_t start = 29 dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(1.0 * NSEC_PER_SEC)); 30 // 马上就执行 31 //
dispatch_time_t start1 = DISPATCH_TIME_NOW; 32 33 //
时间间隔。GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒) 34
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC); 35 36 //
参数:(1)定时器名字 (2)触发时间 (3)时间间隔 (4)0 37
dispatch_source_set_timer(self.timer, start, interval, 0); 38 39
// 4、设置定时器的回调 40
dispatch_source_set_event_handler(self.timer, ^{ 41
NSLog(@"------------%@", [NSThread currentThread]); 42 43 count++;
44 45 if (count == 4) { 46 // 取消定时器 47
dispatch_cancel(self.timer); 48 self.timer = nil; 49 } 50 }); 51 52
// 5、启动定时器 53 dispatch_resume(self.timer); 54 } 55 @end 56 57
打印结果: 58 59 2016-03-24 01:12:39.066
04-掌握-GCD定时器[1179:76865] ------------&lt;NSThread:
0x7f8af0705770&gt;{number = 1, name = main} 60 2016-03-24
01:12:40.067 04-掌握-GCD定时器[1179:76865]
------------&lt;NSThread: 0x7f8af0705770&gt;{number = 1, name =
main} 61 2016-03-24 01:12:41.066 04-掌握-GCD定时器[1179:76865]
------------&lt;NSThread: 0x7f8af0705770&gt;{number = 1, name =
main} 62 2016-03-24 01:12:42.067 04-掌握-GCD定时器[1179:76865]
------------&lt;NSThread: 0x7f8af0705770&gt;{number = 1, name =
main} View Code

 

 

  

------------------------------------------------------------------------

**如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^**    
**作者:蓝田(Loto)**  
**出处:http://www.cnblogs.com/shorfng/**  
**本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。**
** **

------------------------------------------------------------------------

**如有疑问,请发送邮件至 [[email protected]](http://www.bkjia.com/cdn-cgi/l/email-protection) 联系我。**
** ** ** **

** **

   

http://www.bkjia.com/IOSjc/1113415.htmlwww.bkjia.comtruehttp://www.bkjia.com/IOSjc/1113415.htmlTechArticle4.3 多线程进阶篇中(GCD),4.3gcd
更正:队列名称的作用的图中,箭头标注的有些问题,已修正 1.0 GCD简介
GCD概念 :(GrandCentralDispatch)可译...

}

这样既保证了queue里的task1和task2并发执行,又保证了task3同步执行:必须在task1和task2执行完后开始执行。

  • 同步函数 + 串行队列:不会开启新的线程,thread方法所在的线程是main。任务是串行的,执行完1任务,再执2任务,按顺序执行
func nestQueue2() -> Void { print("Hello, World!"); let squeue = DispatchQueue.init(label: "queue_label") squeue.sync { print("task11!----(Thread.current)") squeue.async { print("task22!----(Thread.current)") } sleep print("task33!-----(Thread.current)") } print }
NSLog(@"Thread ----- begin");
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);

// 2.将任务加入队列
dispatch_async(queue, ^{
    NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"2-----%@", [NSThread currentThread]);
});
NSLog(@"Thread ----- end");

图片 8总结

  • 同步函数 + 并发队列:不会开启新的线程,在thread方法所在的线程中,按顺序执行1,2任务

Hello, World!Endtask11!----<NSThread: 0x1457fbe0>{number = 2, name = }task33!-----<NSThread: 0x1457fbe0>{number = 2, name = }task44!-----<NSThread: 0x1457fbe0>{number = 2, name = }task55!-----<NSThread: 0x1457fbe0>{number = 2, name = }task66!-----<NSThread: 0x145800b0>{number = 3, name = }task77!-----<NSThread: 0x1457fbe0>{number = 2, name = }task22!----<NSThread: 0x1457c330>{number = 4, name = }

DISPATCH_QUEUE_PRIORITY_DEFAULT:默认优先级
执行结果:
Thread ----- begin
1-----<NSThread: 0x7fd66ae16720>{number = 3, name = thread1}
2-----<NSThread: 0x7fd66ae16720>{number = 3, name = thread1}
Thread ----- end
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

func nestQueue3() -> Void { print("Hello, World!"); let squeue = DispatchQueue.init(label: "queue_label") squeue.async { print("task11!----(Thread.current)") squeue.sync { print("task22!----(Thread.current)") } print("task33!-----(Thread.current)") } print }

线程间通信

-(void)thread {

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //耗时操作
    dispatch_async(dispatch_get_main_queue(), ^{
        //回到主线程,更新UI
    });
});

咦?队列不是先进先出按顺序一个一个来的吗?没错,并发队列也还是先进先出按顺序的,这里说的并发只是说前面一个任务刚执行还没结束下一个任务马上开始,这中间的速度很快。看起来就像是并发一起执行的,这里只是相对串行队列需要等待前面一个任务执行完后才执行而言的。同样我们还是拿买车票做比喻:我们还是去车站排队买票,不同的是这时排队的窗口不止一个售票员。那么我们前面的那个同志小明先我们一步去了售票员A那里买票了,这时售票员B是空闲的,我们就没必要傻傻的非要等到小明买完了结清账了再去A那买票,我们就直奔B那里了。而小明可能需要买很多张票,他可能帮全家人一起买,而我们潇潇洒洒就买了一张票。等我们买好了出来了,发现小明那里还在刷身份证。这时你不能说我插了小明的队吧,我们还是老老实实排队来的。所以说并行队列也还是按顺序执行的,只是它不需要等待,而且哪个先执行完也是不确定的。还是作个图清楚些:

NSLog(@"Thread ----- begin");
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);

// 2.将任务加入队列
dispatch_sync(queue, ^{
    NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"2-----%@", [NSThread currentThread]);
});
NSLog(@"Thread ----- end");
 let queue = DispatchQueue.init(label: "queue_label") queue.async { print }
NSLog(@"Thread ----- begin");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();

// 2.将任务加入队列
dispatch_async(queue, ^{
    NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    sleep(1.0);
    NSLog(@"4-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"5-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"6-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"7-----%@", [NSThread currentThread]);
});
NSLog(@"Thread ----- end");

图片 9死锁

执行结果:
Thread ----- begin
Thread ----- end
1-----<NSThread: 0x7fd86b706bc0>{number = 3, name = (null)}
2-----<NSThread: 0x7fd86b625900>{number = 2, name = (null)}
1-----<NSThread: 0x7fd86b706bc0>{number = 3, name = (null)}
2-----<NSThread: 0x7fd86b625900>{number = 2, name = (null)}
-(void)thread {

图片 10串行队列写个函数验证下:

}

结果在意料之中,主线程没有被阻塞,Hello, World! End执行不受queue里任务影响。queue队列开启了3个线程,任务是并发执行的。这里也验证了之前说的另一个结论:并不是有多少任务就开启多少线程的,task1和task4都是线程2。为了更明显看出这种结果,现在在原先代码了多增加几个任务。

执行结果:
Thread ----- begin
1-----<NSThread: 0x7faf10604e80>{number = 1, name = main}
2-----<NSThread: 0x7faf10604e80>{number = 1, name = main}
Thread ----- end
-(void)thread {

图片 11调度线程

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(thread) object:nil];
thread.name = @"thread1";
[thread start];

Hello, World!Endtask1!----<NSThread: 0x155a1e10>{number = 2, name = }task2!----<NSThread: 0x155a1e10>{number = 2, name = }task3!-----<NSThread: 0x155a1e10>{number = 2, name = }task4!-----<NSThread: 0x155ae030>{number = 3, name = }

-(void)thread{

func cAsync() -> Void { print("Hello, World!"); let queue = DispatchQueue.init(label: "queue_label", attributes: .concurrent) queue.async { print("task11!----(Thread.current)") } queue.async { sleep print("task22!----(Thread.current)") } queue.async { print("task33!-----(Thread.current)") } queue.async { print("task44!-----(Thread.current)") } print }

dispatch_async_f

一般用dispatch_async(block)。dispatch_async_f(函数)
执行结果:dsafasd

-(void)asuncf{

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSString *str = @"dsafasd";
dispatch_async_f(queue, (__bridge void *)(str), download);

}

void download(void * data)
{
NSLog(@"%@", data);
}

func barrier() -> Void { print("Hello, World!"); let queue = DispatchQueue.init(label: "queue_label", attributes: .concurrent) queue.async { sleep print("task11!----(Thread.current)") } queue.async { sleep print("task22!----(Thread.current)") } queue.sync(flags: .barrier) { // 设置flags为barrier print("barrier!-----(Thread.current)") } queue.async { sleep print("task33!-----(Thread.current)") } queue.async { sleep print("task44!-----(Thread.current)") } print }

}

之前的主队列同步/异步执行就属于嵌套任务的情况,只是嵌套的是主队列。我们看下自己创建的队列嵌套任务的情况:

  • 同步函数 + 主队列:
    执行dispatch_sync时主线程堵塞
    执行结果:Thread ----- begin
    -(void)thread {
 func sQueueSync() -> Void { print("Hello, World!"); let queue = DispatchQueue.init(label: "queue_label") queue.sync { print("task1!----(Thread.current)") } queue.sync { sleep print("task2!----(Thread.current)") } queue.sync { sleep print("task3!-----(Thread.current)") } queue.sync { print("task4!-----(Thread.current)") } print }

dispatch_group

因为是在并行队列上执行,追加字符串的2个任务执行顺不不确定,当并行队列全部执行完成后,最后到dispatch_group_notify执行操作。

执行结果:123456
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, copy) NSString *str;
@end
-(void)group {

// 获取全局列组
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 创建一个队列组
dispatch_group_t group = dispatch_group_create();
self.str = @"";
dispatch_group_async(group, queue, ^{
   self.str = [self.str stringByAppendingString:@"123"];
});

dispatch_group_async(group, queue, ^{
    self.str = [self.str stringByAppendingString:@"456"];
});

dispatch_group_notify(group, queue, ^{
    NSLog(@"%@", self.str);
    dispatch_async(dispatch_get_main_queue(), ^{
        //主线程操作
    });
});

}

使用barrier确保了task1,task2和task3,task4并发执行,但同时可以确保barrier后的task3,task4在barrier前的task1,task2后面执行。很明显可以看出,barrier和group作用是类似的,就是通过阻塞queue来实现同步功能的,这段代码同样可以使用group实现。但barrier更方便。

Thread ----- begin
Thread ----- end
1-----<NSThread: 0x7f917044f030>{number = 3, name = (null)}
2-----<NSThread: 0x7f917044f030>{number = 3, name = (null)}
-(void)thread {

  • 串行队列异步执行
  • 主队列异步执行
  • 串行队列同步执行
  • 主队列同步执行歇了一口气,接着讲并行队列
  • 同步函数 + 主队列
  • 异步函数 + 主队列
  • 同步函数 + 串行队列
  • 异步函数 + 串行队列
  • 同步函数 + 并发队列
  • 异步函数 + 并发队列
  • 线程间通信
  • dispatch_barrier_async(栅栏)
  • dispatch_after 延迟执行
  • dispatch_apply 多线程遍历
  • dispatch_once
  • dispatch_async_f
  • dispatch_group
  • dispatch_source定时器
  • dispatch_group_enter/dispatch_group_leave/dispatch_group_notify

Hello, World!task11!----<NSThread: 0x14e3a7c0>{number = 1, name = main}task22!----<NSThread: 0x14e3a7c0>{number = 1, name = main}task33!-----<NSThread: 0x14e3a7c0>{number = 1, name = main}task44!-----<NSThread: 0x14e3a7c0>{number = 1, name = main}End

NSLog(@"Thread ----- begin");
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 2.将任务加入队列
dispatch_sync(queue, ^{
    NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"2-----%@", [NSThread currentThread]);
});
NSLog(@"Thread ----- end");

DisPatchGroup是GCD中一个组的概念,可以把相关的任务归并到一个组内来执行,通过监听组内所有任务的执行情况来做相应处理。

  • 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完1任务,再执行2任务,按顺序执行
    执行结果:

可以看到,串行队列异步执行和并行队列异步执行才是最实用的,这也是我们平时项目里用的最多的。具体用哪种,就要看你是想并发执行还是顺序执行了。有了这个基础后,我们再来分析嵌套任务的情况:

}

图片 12barrier

  • 异步函数 + 主队列:只在主线程中执行任务

对比之前串行队列的图示,区别还是比较明显的。和串行队列一样,并行队列也分同步执行、异步执行。

dispatch_barrier_async:

dispatch_barrier_async,可以翻译成栅栏(barrier)
barrier作为流程控制的一种方式是用在并行环境当中。

以barrier任务为分界线,先执行barrier之前的任务1,2(1,2执行顺序不确定),在执行3,4(3,4执行顺序不确定)
执行结果:
----2-----<NSThread: 0x7f9982503140>{number = 6, name = (null)}
----1-----<NSThread: 0x7f9982748d90>{number = 5, name = (null)}
----barrier-----<NSThread: 0x7f9982748d90>{number = 5, name = (null)}
----3-----<NSThread: 0x7f9982748d90>{number = 5, name = (null)}
----4-----<NSThread: 0x7f9982503140>{number = 6, name = (null)}
-(void)barrier {

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----2-----%@", [NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"----barrier-----%@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----4-----%@", [NSThread currentThread]);
});

}

注释掉dispatch_barrier_async后任务1-4执行顺序无序
执行结果:
----1-----<NSThread: 0x7f94d3725df0>{number = 3, name = (null)}
----4-----<NSThread: 0x7f94d379a910>{number = 5, name = (null)}
----3-----<NSThread: 0x7f94d3463810>{number = 4, name = (null)}
----2-----<NSThread: 0x7f94d344f320>{number = 2, name = (null)}
-(void)barrier {

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----2-----%@", [NSThread currentThread]);
});

// dispatch_barrier_async(queue, ^{
// NSLog(@"----barrier-----%@", [NSThread currentThread]);
// });

dispatch_async(queue, ^{
    NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----4-----%@", [NSThread currentThread]);
});

}

Hello, World!task11!----<NSThread: 0x16e50940>{number = 2, name = }task22!----<NSThread: 0x16e50ca0>{number = 3, name = }barrier!-----<NSThread: 0x16e2b1e0>{number = 1, name = main}Endtask33!-----<NSThread: 0x16e50940>{number = 2, name = }task44!-----<NSThread: 0x16e50ca0>{number = 3, name = }

  • 异步函数 + 并发队列:可以同时开启多条线程

前面说了,队列负责调度线程。而串行队列只能调度一个线程,但并行队列是可以调度多个线程的。这就是它们本质的区别。

同步执行不会开启线程,任务执行需要等待前面任务完成。

}

毫无疑问,它也会发生死锁:

dispatch_once

dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题。

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    //程序运行期间只会执行一次
});

利用dispatch_once创建单例模式
static id _instance;
+(instancetype)allocWithZone:(struct _NSZone *)zone{

static dispatch_once_t onceToken;//记录是否执行了block
dispatch_once(&onceToken, ^{
    _instance = [super allocWithZone:zone];
});
return _instance;

}
+(instancetype)shareInstance{

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    _instance = [[self alloc] init];
});
return _instance;

}
-(id)copyWithZone:(NSZone *)zone{

return _instance;

}

本文由乐虎游戏发布于计算机资讯,转载请注明出处:4.3 多线程进阶篇&amp;lt;中&amp;gt;(GCD),4.3gcd

关键词:

CocoaPods 系列(三)—— 使用

后天给我们狼吞虎餐上面几点内容。 类型组件化、平台化是技能集团的一道目的,越来越多的技能公司体贴使用pod管...

详细>>

计算机在iOS开垦中应用Protobuf

Protobuf简介 protocolbuffer 是google的一种数据交换的格式,它独立于语言,独立于平台。google提供了多种语言的实现:j...

详细>>

iOS 面试题一

出处:先是程序员,然后才是iOS程序员 —写给广大非科班iOS开发者的一篇面试总结如果让你实现属性的weak,如何实现...

详细>>

Android 滚轮选择器的实现详解

之前项目中需要实现这样一个功能,效果如图所示: 简介 最近用一个日期选择控件,感觉官方的DatePicker操作有点复杂...

详细>>