iOS多线程之GCD

1. GCD相关概念

任务:就是执行操作的意思,就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:『同步执行』『异步执行』。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。

同步执行(sync):同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。只能在当前线程中执行任务,不具备开启新线程的能力。

异步执行(async):异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。可以在新的线程中执行任务,具备开启新线程的能力。

队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。

串行队列(Serial Dispatch Queue):每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)

并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)。并发队列 的并发功能只有在异步(dispatch_async)方法下才有效。

2. GCD 的使用步骤

1.创建一个队列(串行队列或并发队列);
2.将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)。

3. 队列的创建方法 / 获取方法

使用 dispatch_queue_create 方法来创建队列。该方法需要传入两个参数,第一个参数表示队列的唯一标识符,用于 DEBUG,可为空。第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列。
// 串行队列的创建方法
dispatch_queue_t  queue = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t  queue = dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);

对于串行队列,GCD 提供了的一种特殊的串行队列:『主队列(Main Dispatch Queue)』。所有放在主队列中的任务,都会放到主线程中执行。可使用 dispatch_get_main_queue() 方法获得主队列。
// 主队列的获取方法
dispatch_queue_t queue = dispatch_get_main_queue();

对于并发队列,GCD 默认提供了 『全局并发队列(Global Dispatch Queue)』。
可以使用 dispatch_get_global_queue 方法来获取全局并发队列。需要传入两个参数。第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用 0 即可。
// 全局并发队列的获取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

4. 任务的创建方法

// 同步执行任务创建方法
dispatch_sync(queue, ^{
    
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
    
});

5.1 队列和任务的组合使用

区别 并发队列 串行队列 主队列
同步(sync) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 死锁,卡住不执行
异步(async) 有开启新线程,并发执行任务 有开启新线程(1条),串行执行任务 没有开启新线程,串行执行任务
从上边可看出:『主线程』 中调用 『主队列』+『同步执行』 会导致死锁问题。
这是因为 主队列中追加的同步任务 和 主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题。
而如果我们在 『其他线程』 调用 『主队列』+『同步执行』,则不会阻塞 『主队列』,自然也不会造成死锁问题。最终的结果是:不会开启新线程,串行执行任务。

5.2 队列嵌套情况下,不同组合方式区别

区别 『异步执行+并发队列』嵌套『同一个并发队列』 『同步执行+并发队列』嵌套『同一个并发队列』 『异步执行+串行队列』嵌套『同一个串行队列』 『同步执行+串行队列』嵌套『同一个串行队列』
同步(sync) 没有开启新的线程,串行执行任务 没有开启新线程,串行执行任务 死锁卡住不执行 死锁卡住不执行
异步(async) 有开启新线程,并发执行任务 有开启新线程,并发执行任务 有开启新线程(1 条),串行执行任务 有开启新线程(1 条),串行执行任务

6. 具体代码组合使用详情 ( 线程number=1 就是主线程 大于1就是子线程 )

  1. 同步(sync) + 并发队列 ( 没有开启子线程,串行执行任务,在主线程 )

//  同步(sync) + 并发队列 (没有开启子线程,串行执行任务,在主线程)

-(void)startGCDAction{
    
    // 1.1 创建并发队列
 // dispatch_queue_t  queueObj = dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
    // 1.2 获取全局并发队列
    dispatch_queue_t  queueObj = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2.0 同步
    dispatch_sync(queueObj, ^{
        NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
    });
}

最终运行结果:

TestModel[85321:13209706] 做任务(1),当前线程:{number = 1, name = main}
TestModel[85321:13209706] 做任务(2),当前线程:{number = 1, name = main}
TestModel[85321:13209706] 做任务(3),当前线程:{number = 1, name = main}
 
  1. 异步(async) + 并发队列 ( 有开启子线程,并发执行任务,在子线程 )

// 异步(async) + 并发队列(有开启子线程,并发执行任务,在子线程)

-(void)startGCDAction{
    
     // 1.1 创建并发队列
     dispatch_queue_t  queueObj = dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
     // 1.2 获取全局并发队列
//    dispatch_queue_t  queueObj = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.0 异步
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
    });
}

最终运行结果:

TestModel[85332:13211973] 做任务(2),当前线程:{number = 4, name = (null)}
TestModel[85332:13211979] 做任务(1),当前线程:{number = 3, name = (null)}
TestModel[85332:13211973] 做任务(3),当前线程:{number = 4, name = (null)}
  1. 同步(sync) + 串行队列 ( 没有开启子线程,串行执行任务,在主线程 )

// 同步(sync) + 串行队列 (没有开启子线程,串行执行任务,在主线程)

-(void)startGCDAction{
    
    // 1.0 创建串行队列
    dispatch_queue_t  queueObj = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
    
    // 2.0 同步
    dispatch_sync(queueObj, ^{
        NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
    });
}    

最终运行结果:

TestModel[85392:13219612] 做任务(1),当前线程:{number = 1, name = main}
TestModel[85392:13219612] 做任务(2),当前线程:{number = 1, name = main}
TestModel[85392:13219612] 做任务(3),当前线程:{number = 1, name = main}
  1. 异步(async) + 串行队列 ( 只开启一条子线程,串行执行任务 )

// 异步(async) + 串行队列 (只开启一条子线程,串行执行任务)

-(void)startGCDAction{
    
    // 1.0 创建串行队列
    dispatch_queue_t  queueObj = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
    
    // 2.0 异步
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
    });
    
}

最终运行结果:

TestModel[85396:13221073] 做任务(1),当前线程:{number = 3, name = (null)}
TestModel[85396:13221073] 做任务(2),当前线程:{number = 3, name = (null)}
TestModel[85396:13221073] 做任务(3),当前线程:{number = 3, name = (null)}

  1. 同步(sync) + 主线程 ( 造成死锁,主线程和同步任务相互等待 )
   
   // 同步(sync) + 主线程 (造成死锁,主线程和同步任务相互等待)
   
   -(void)startGCDActionWithMainThread{
       
       // 1.0 获取主线程
       dispatch_queue_main_t  mainQueue = dispatch_get_main_queue();
       
       // 2.0 同步
       dispatch_sync(mainQueue, ^{
           NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
       });
       
       dispatch_sync(mainQueue, ^{
           NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
       });
       
       NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
       
   }

   最终运行结果:
   
   Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1004cb84c)
     
   在 dispatch_sync(mainQueue, ^{ }); 报上诉错误 三个都不会打印 因为程序直接奔溃

  1. 异步(async) + 主线程 ( 没有开启子线程,串行执行任务,在主线程 )

// 异步(async) + 主线程 (没有开启子线程,串行执行任务,在主线程)

-(void)startGCDActionWithMainThread{
    
    // 1.0 获取主线程
    dispatch_queue_main_t  mainQueue = dispatch_get_main_queue();
    
    // 2.0 异步
    dispatch_async(mainQueue, ^{
        NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(mainQueue, ^{
        NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
    });
}

最终运行结果:

TestModel[85332:13211973] 做任务(1),当前线程:{number = 1, name = main}
TestModel[85332:13211973] 做任务(2),当前线程:{number = 1, name = main}

7. GCD 线程之间的通信

在其子线程中完成了耗时操作后,需要回到主线程 (进行 UI 刷新) ,那么就用到了线程之间的通讯。通过使用

1.1 获取主线程 dispatch_queue_t mainQueue = dispatch_get_main_queue();

1.2 异步回到主线程 dispatch_async(mainQueue, ^{ } );

// 线程之间的通讯(子线程回到主线程)
-(void)startGCDCommunicationAction{
    
    // 1.1 创建并发队列
    // dispatch_queue_t queueObj=dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
    // 1.2 获取全局并发队列
    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.0 异步
    dispatch_async(queueObj, ^{
        
        NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:3.0];  // 睡眠线程3秒,相当于耗时操作
        
        // 3.0 获取主线程
        dispatch_queue_t mainQueue=dispatch_get_main_queue();
        
        // 4.0 异步回到主线程
        dispatch_async(mainQueue, ^{
            
            NSLog(@"回到主线程刷新UI操作,当前线程:%@",[NSThread currentThread]);
            
        });
        
    });
   
}

最终运行结果:

2019-08-11 12:26:51.973353 TestModel[85877:13291589] 做任务(1),当前线程:{number = 4, name = (null)}
2019-08-11 12:26:54.979122 TestModel[85877:13291523] 回到主线程刷新UI操作,当前线程:{number = 1, name = main}

8. GCD 的其他方法

1. 栅栏函数(方法):dispatch_barrier_sync (同步 ) 和 dispatch_barrier_async (异步 ) 执行完栅栏前面的操作之后,才执行栅栏操作(子线程或主线程),最后再执行栅栏后边的操作。

// 使用栅栏函数,必须通过创建并发队列 不能使用全局并发队列
-(void)startGCDWithBarrier{
    
    // 1.0 创建并发队列 (这里不能用下面的 直接获取全局并发队列 只能通过创建才能体现出栅栏函数)
    dispatch_queue_t queueObj=dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);
    
    // 1.0 获取全局并发队列
//    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.0 异步操作1
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];  // 睡眠线程1秒
    });
    // 2.0 异步操作2
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];  // 睡眠线程2秒
    });
    
    // 3.0 同步栅栏函数(在主线程执行操作,换成 async 就在子线程中)
    dispatch_barrier_sync(queueObj, ^{
        NSLog(@"栅栏函数做任务(),当前线程:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];  // 睡眠线程2秒
    });
    
    // 2.0异步操作3
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
    });
    // 2.0异步操作4
    dispatch_async(queueObj, ^{
        NSLog(@"做任务(4),当前线程:%@",[NSThread currentThread]);
    });
   
}

最终运行结果:

2019-08-11 12:49:37.631269 TestModel[85906:13296017] 做任务(1),当前线程:{number = 4, name = (null)}
2019-08-11 12:49:38.632798 TestModel[85906:13296017] 做任务(2),当前线程:{number = 4, name = (null)}
2019-08-11 12:49:40.634186 TestModel[85906:13295953] 栅栏函数做任务(),当前线程:{number = 1, name = main}
2019-08-11 12:49:41.635503 TestModel[85906:13296017] 做任务(3),当前线程:{number = 4, name = (null)}
2019-08-11 12:49:41.635610 TestModel[85906:13296017] 做任务(4),当前线程:{number = 4, name = (null)}

2. 延时执行函数(方法):dispatch_after 其并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中(一定在主线程)。

// GCD延时函数
-(void)startGCDWithAfterFun{
    
    NSLog(@"来咯!来咯!她们都来了  ");
    
    // 获取全局的并发队列
    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queueObj, ^{
        
        NSLog(@"调用前的线程是:%@ ",[NSThread currentThread]);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
            NSLog(@"3秒后才调用我这个操作,当前线程是:%@ ",[NSThread currentThread]);
            
        });
        
    });
}

最终运行结果:

2019-08-11 13:09:48.371427 TestModel[85920:13298751] 来咯!来咯!她们都来了  
2019-08-11 13:09:48.371942 TestModel[85920:13298827] 调用前的线程是:{number = 4, name = (null)} 
2019-08-11 13:09:51.613677 TestModel[85920:13298751] 3秒后才调用我这个操作,当前线程是:{number = 1, name = main} 

3. 一次性函数(方法):dispatch_once 该函数中的代码整个程序运行过程中只会执行一次。

// GCD一次性函数(只会执行一次)
-(void)startGCDWithOnceFun{
    
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        
        NSLog(@"你只能打我一次哦(),不服来战!");
    });
    
}


我在 -(void)viewDidLoad {} 中调用多次,最终结果只会输出一次

-(void)viewDidLoad {
    [super viewDidLoad];
  
    [self startGCDWithOnceFun];
    [self startGCDWithOnceFun];
    [self startGCDWithOnceFun];
    [self startGCDWithOnceFun];
}

最终运行结果:

TestModel[85923:13300176] 你只能打我一次哦(),不服来战!

4. GCD 队列组 dispatch_group_async 分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。监听队列组中任务完成 dispatch_group_notify

// GCD之队列组
-(void)startGCDWithGroup{
    
    
    // 1.0 创建一个队列组
    dispatch_group_t groupObj=dispatch_group_create();
    
    // 2.1 创建并发队列
    dispatch_queue_t queueObj=dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);
    
    // 2.2 获取全局的并发队列
//    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
   // 3.1 使用队列组执行任务(1)
    dispatch_group_async(groupObj, queueObj, ^{
        NSLog(@"列组中,子线程执行任务(1),当前线程是:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];
        
    });
    
    // 3.1 使用队列组执行任务(1)
    dispatch_group_async(groupObj, queueObj, ^{
        NSLog(@"列组中,子线程队执行任务(2),当前线程是:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3.0];
        
    });
    
    // 4.0 队列组中任务执行完毕 回到主线程
    dispatch_group_notify(groupObj, dispatch_get_main_queue(), ^{
        NSLog(@"队列组中任务执行完毕调用,回到主线程,当前线程是:%@",[NSThread currentThread]);
    });
  
}

最终运行结果:

2019-08-11 17:07:44.674 TestModel[86321:13347506] 列组中,子线程队执行任务(2),当前线程是:{number = 3, name = (null)}
2019-08-11 17:07:44.675 TestModel[86321:13347505] 列组中,子线程执行任务(1),当前线程是:{number = 4, name = (null)}
2019-08-11 17:07:47.680 TestModel[86321:13347495] 队列组中任务执行完毕调用,回到主线程,当前线程是:{number = 1, name = main}

5. dispatch_group_wait : 暂停当前线程(阻塞当前线程) dispatch_semaphore :(线程加锁)

你可能感兴趣的:(iOS多线程之GCD)