美文网首页
iOS多线程之GCD

iOS多线程之GCD

作者: 践行者_Leng | 来源:发表于2019-08-19 16:26 被阅读0次

    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),当前线程:<NSThread: 0x281beda40>{number = 1, name = main}
    TestModel[85321:13209706] 做任务(2),当前线程:<NSThread: 0x281beda40>{number = 1, name = main}
    TestModel[85321:13209706] 做任务(3),当前线程:<NSThread: 0x281beda40>{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),当前线程:<NSThread: 0x281405a80>{number = 4, name = (null)}
    TestModel[85332:13211979] 做任务(1),当前线程:<NSThread: 0x281408cc0>{number = 3, name = (null)}
    TestModel[85332:13211973] 做任务(3),当前线程:<NSThread: 0x281405a80>{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),当前线程:<NSThread: 0x2822a5a40>{number = 1, name = main}
    TestModel[85392:13219612] 做任务(2),当前线程:<NSThread: 0x2822a5a40>{number = 1, name = main}
    TestModel[85392:13219612] 做任务(3),当前线程:<NSThread: 0x2822a5a40>{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),当前线程:<NSThread: 0x28106c940>{number = 3, name = (null)}
    TestModel[85396:13221073] 做任务(2),当前线程:<NSThread: 0x28106c940>{number = 3, name = (null)}
    TestModel[85396:13221073] 做任务(3),当前线程:<NSThread: 0x28106c940>{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),当前线程:<NSThread: 0x280406e40>{number = 1, name = main}
    TestModel[85332:13211973] 做任务(2),当前线程:<NSThread: 0x280406e40>{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),当前线程:<NSThread: 0x282f797c0>{number = 4, name = (null)}
    2019-08-11 12:26:54.979122 TestModel[85877:13291523] 回到主线程刷新UI操作,当前线程:<NSThread: 0x282f1ae40>{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),当前线程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
    2019-08-11 12:49:38.632798 TestModel[85906:13296017] 做任务(2),当前线程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
    2019-08-11 12:49:40.634186 TestModel[85906:13295953] 栅栏函数做任务(😁😁),当前线程:<NSThread: 0x2806d3040>{number = 1, name = main}
    2019-08-11 12:49:41.635503 TestModel[85906:13296017] 做任务(3),当前线程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
    2019-08-11 12:49:41.635610 TestModel[85906:13296017] 做任务(4),当前线程:<NSThread: 0x2806b1a00>{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] 调用前的线程是:<NSThread: 0x280b38f80>{number = 4, name = (null)} 
    2019-08-11 13:09:51.613677 TestModel[85920:13298751] 3秒后才调用我这个操作,当前线程是:<NSThread: 0x280b55880>{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),当前线程是:<NSThread: 0x281365880>{number = 3, name = (null)}
    2019-08-11 17:07:44.675 TestModel[86321:13347505] 列组中,子线程执行任务(1),当前线程是:<NSThread: 0x281330940>{number = 4, name = (null)}
    2019-08-11 17:07:47.680 TestModel[86321:13347495] 队列组中任务执行完毕调用,回到主线程,当前线程是:<NSThread: 0x281351340>{number = 1, name = main}
    
    

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

    相关文章

      网友评论

          本文标题:iOS多线程之GCD

          本文链接:https://www.haomeiwen.com/subject/pmcesctx.html