Giter Club home page Giter Club logo

gcd_demo's Introduction

前言

各路大神对GCD的原理解析和使用方法网上到处都是,可以轻松搜索到。那为什么笔者还要自己动手写一篇所谓的"葵花宝典"呢?其实本篇文章主要是普及了一些基础知识概念。例如队列、线程、异步同步的区别,搞懂这些才是弄明白GCD的条件。笔者搜集了GCD的绝大部分api,包括不常用的、冷门的。这里没有高大上的实现原理,没有难懂的底层实现。以最浅显的,最简单的文字说明配上demo和代码实例最后结合运行log,让你轻松不费脑力的理解GCD的应用场景和操作姿势。

基础概念

关于GCD:

(1)是基于c语言的底层api
(2)用block定义任务,使用起来非常灵活便捷
(3)GCD会自动利用更多的CPU内核(比如双核、四核)
(4)GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
(5)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

关于进程:

正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间;

关于线程:

线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程。

关于队列:

队列用来存放任务,一种符合 FIFO(先进先出)原则的数据结构,线程的创建和回收不需要程序员操作,由队列负责。

串行队列:队列中的任务只会顺序执行,一个任务执行完毕后,再执行下一个任务
并发队列:队列中的多个任务并发(同时)执行,而且无法确定任务的执行顺序     
全局队列:是系统开发的,直接拿过来用就可以;与并行队列类似,但调试时,无法确认操作所在队列
主队列:  每一个应用程序对应唯一一个主队列,是gcd中自带的一种特殊的串行队列,直接获取即可。放在主队列中的任务,都会在主线程中执行。在多线程开发中,使用主队列更新UI。

关于操作:

dispatch_async 异步操作,在新的线程中执行任务,具备开启新线程的能力(不是百分百开启新线程,会取决于任务所在队列类型),会并发执行,无法确定任务的执行顺序;
dispatch_sync  同步操作,在当前线程中执行任务,不具备开启新线程的能力,会依次顺序执行;
  • 图例:

使用姿势

分为两步:

第一步:创建一个队列;

第二步:将任务放到队列中;

三个关键点:

第一点:任务内容;

第二点:队列类别;

第三点:操作(追加)姿势;

队列和任务

1. 获取队列:

GCD中大体可以分为三种队列:

  • 串行队列: dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);

  • 并发队列:

    • 一般并发队列 dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
    • 全局并发队列可以作为普通并发队列来使用 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 主队列: dispatch_queue_t queue = dispatch_get_main_queue();

2. 操作(追加)方式:

  • 同步执行任务创建方法
dispatch_sync(queue, ^{
    NSLog(@"我是任务");
});
  • 异步执行任务创建方法
dispatch_async(queue, ^{
     NSLog(@"我是任务");
});

3. 队列 + 任务:

3.1 队列 + 任务的六种组合

看到这里你不难发现,GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async,配合上述的三种队列形式(串行队列、并发队列、主队列),那么就会存在六种不同的多线程使用方法,如下:

  • 同步执行 & 并发队列 : 不新建线程,在当前线程中顺序执行
  • 异步执行 & 并发队列 : 新建多个新线程,线程会复用,无序执行
  • 同步执行 & 串行队列 : 在当前线程中顺序执行
  • 异步执行 & 串行队列 : 新建一条新的线程,在该线程中顺序执行
  • 异步执行 & 主队列 : 不新建线程,在主线程中顺序执行
  • 同步执行 & 主队列(在主线程中会crash): 主线程中会产生死锁

3.2 各种组合的使用方法
同步执行 & 并发队列:
-(void)syncAndConcurrentqueue{
    
     NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);
    
    //下面提供两种并发队列的获取方式,其运行结果无差别,所以归为了一类,你可以自由选择
    dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    // 全局并发队列
    //    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 第一个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第二个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第三个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);
 }
  • 输出结果:

  • 总结:只会在当前线程中依次执行任务,不会开启新线程,执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。
异步执行 & 并发队列:
-(void)asyncAndConcurrentqueue{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    //下面提供两种并发队列的获取方式,其运行结果无差别,所以归为了一类,你可以自由选择
    
    //dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    // 全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 第一个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第二个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第三个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

  • 输出结果:

  • 总结:从log中可以发现,系统另外开启了3个线程,并且任务是同时执行的,并不是按照1>2>3顺序执行。所以异步+并发队列具备开启新线程的能力,且并发队列可开启多个线程,同时执行多个任务。
同步执行 & 串行队列:
-(void)syncAndSerialqueue{
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
    
    // 第一个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第二个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第三个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

  • 输出结果:

  • 总结:只会在当前线程中依次执行任务,不会开启新线程,执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。
异步执行 & 串行队列:
-(void)asyncAndSerialqueue{
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);
    
    dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
    
    // 第一个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第二个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第三个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);
    
}
  • 输出结果:

  • 总结:开启了一条新线程,异步执行具备开启新线程的能力,因为是串行队列所以只开启一个线程,在该线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。

异步执行 & 主队列:
-(void)asyncAndMainqueue{
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);
    
    //获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 第一个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第二个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第三个任务
    dispatch_async(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);
    
}
  • 输出结果:

  • 总结:所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中),在主线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。

同步执行 & 主队列(在主线程中会crash):
-(void)syncAndMainqueue{
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);
    
    //获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 第一个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第二个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    // 第三个任务
    dispatch_sync(queue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);    
}

//下面的例子类似:在同一个同步串行队列中,再使用该队列同步执行任务也是会发生死锁。
-(void)syncAndMainqueue1{

    dispatch_queue_t queue1 = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue1, ^{
        
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----11111-----当前线程%@",[NSThread currentThread]);//到这里就死锁了
        
        dispatch_sync(queue1, ^{
            
            [NSThread sleepForTimeInterval:2];
            
            NSLog(@"----22222---当前线程%@",[NSThread currentThread]);
        });
        
        NSLog(@"----333333-----当前线程%@",[NSThread currentThread]);
        
    });
    NSLog(@"----44444-----当前线程%@",[NSThread currentThread]);
}
    
  • 输出结果:

  • 总结:直接crash。这是因为发生了死锁,在gcd中,禁止在主队列(串行队列)中再以同步操作执行主队列任务。同理,在同一个同步串行队列中,再使用该队列同步执行任务也是会发生死锁。

同步执行 & 主队列(在其它线程中):
-(void)othersyncAndMainqueue{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);
    
    dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
    
    // 第一个任务
    dispatch_async(queue, ^{
        
        NSLog(@"----执行任务---%@",[NSThread currentThread]);
        
        //获取主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 第一个任务
        dispatch_sync(queue, ^{
            
            //这里线程暂停2秒,模拟一般的任务的耗时操作
            [NSThread sleepForTimeInterval:2];
            
            NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
        });
    });
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);
    
}
  • 输出结果:

  • 总结:所有任务都是在主线程(非当前线程)中执行的,没有开启新的线程(所有放在主队列中的任务,都会放到主线程中执行)。在主线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。

GCD常用API及其使用方法

1. Dispatch Queue:

//各种队列的获取方法
-(void)getQueue{
    
    //主队列的获取方法:主队列是串行队列,主队列中的任务都将在主线程中执行
    dispatch_queue_t mainqueue = dispatch_get_main_queue();
    
    //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
    
    //并发队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    //全局并发队列的获取方法:第一个参数表示队列优先级,我们选择默认的好了,第二个参数flags作为保留字段备用,一般都直接填0
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}

2. dispatch_queue_creat

//自定义队列的创建方法
-(void)queue_create{
    //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
    
    //并发队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
}

3. dispatch_set_target_queue

  • dispatch_set_target_queue可以更改Dispatch Queue的执行优先级 dispatch_queue_create函数生成的DisPatch Queue不管是Serial DisPatch Queue还是Concurrent Dispatch Queue,执行的优先级都与默认优先级的Global Dispatch queue相同,如果需要变更生成的Dispatch Queue的执行优先级则需要使用dispatch_set_target_queue函数。
//使用dispatch_set_target_queue更改Dispatch Queue的执行优先级
-(void)testTargetQueue1{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", NULL);
    
    //指定一个任务
    dispatch_async(seriaQueue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    //全局并发队列的获取方法:第一个参数表示队列优先级,我们选择默认的好了,第二个参数flags作为保留字段备用,一般都直接填0
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //指定一个任务
    dispatch_async(globalQueue, ^{
        
        //这里线程暂停2秒,模拟一般的任务的耗时操作
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    
    //第一个参数为要设置优先级的queue,第二个参数是参照物,即将第一个queue的优先级和第二个queue的优先级设置一样。
    //第一个参数如果是系统提供的【主队列】或【全局队列】,则不知道会出现什么情况,因此最好不要设置第一参数为系统提供的队列
    dispatch_set_target_queue(seriaQueue,globalQueue);
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

//dispatch_set_target_queue除了能用来设置队列的优先级之外,还能够创建队列的层次体系,当我们想让不同队列中的任务同步的执行时,我们可以创建一个串行队列,然后将这些队列的target指向新创建的队列即可。
- (void)testTargetQueue2 {
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_queue_t targetQueue = dispatch_queue_create("com.test.target_queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t queue1 = dispatch_queue_create("com.test.queue1", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t queue2 = dispatch_queue_create("com.test.queue2", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_set_target_queue(queue1, targetQueue);
    
    dispatch_set_target_queue(queue2, targetQueue);
    
    //指定一个异步任务
    dispatch_async(queue1, ^{
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    
    //指定一个异步任务
    dispatch_async(queue2, ^{
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    
    //指定一个异步任务
    dispatch_async(queue2, ^{
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

4. dispatch_after:

//延时执行,需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。
-(void)dispatch_after{
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2秒后异步追加任务代码到主队列等待执行
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

5. dispatch_once:

//只执行一次,通常在创建单例时使用,多线程环境下也能保证线程安全
-(void)dispatch_once_1{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"----只执行一次的任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

6. dispatch_apply:

//快速遍历方法,可以替代for循环的函数。dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。
//会创建新的线程,并发执行
-(void)dispatch_apply{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_apply(100, globalQueue, ^(size_t index) {
        NSLog(@"执行第%zd次的任务---%@",index, [NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

7. dispatch_group:

//队列组:当我们遇到需要异步下载3张图片,都下载完之后再拼接成一个整图的时候,就需要用到gcd队列组。
-(void)dispatch_group{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 第一个任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
        
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 第二个任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        // 第三个任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行最后的汇总任务---当前线程%@",[NSThread currentThread]);
    });
    
    //若想执行完上面的任务再走下面这行代码可以加上下面这句代码
    
    // 等待上面的任务全部完成后,往下继续执行(会阻塞当前线程)
    //    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);
}

//dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1
//dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。
//当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。
-(void)dispatch_group_1{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 第一个任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
        
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 第二个任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
        
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 第三个任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
        
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        
        [NSThread sleepForTimeInterval:2];
        NSLog(@"----执行最后的汇总任务---当前线程%@",[NSThread currentThread]);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

8. dispatch_semaphore:

//信号量
//总结:信号量设置的是2,在当前场景下,同一时间内执行的线程就不会超过2,先执行2个线程,等执行完一个,下一个会开始执行。
-(void)dispatch_semaphore{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //任务1
    dispatch_async(queue, ^{
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        NSLog(@"----开始执行第一个任务---当前线程%@",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----结束执行第一个任务---当前线程%@",[NSThread currentThread]);
        
        dispatch_semaphore_signal(semaphore);
    });
    
    //任务2
    dispatch_async(queue, ^{
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        NSLog(@"----开始执行第二个任务---当前线程%@",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:1];
        
        NSLog(@"----结束执行第二个任务---当前线程%@",[NSThread currentThread]);
        
        dispatch_semaphore_signal(semaphore);
    });
    
    //任务3
    dispatch_async(queue, ^{
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        NSLog(@"----开始执行第三个任务---当前线程%@",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----结束执行第三个任务---当前线程%@",[NSThread currentThread]);
        
        dispatch_semaphore_signal(semaphore);
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

9. Dispatch I/O:

//以下为苹果中使用Dispatch I/O和Dispatch Data的例子
pipe_q = dispatch_queue_create("PipeQ",NULL);
pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM,fd,pipe_q,^(int err){
   close(fd);
});

*out_fd = fdpair[i];

dispatch_io_set_low_water(pipe_channel,SIZE_MAX);

dispatch_io_read(pipe_channel,0,SIZE_MAX,pipe_q, ^(bool done,dispatch_data_t pipe data,int err){
   if(err == 0)
     {
       size_t len = dispatch_data_get_size(pipe data);
       if(len > 0)
       {
          const char *bytes = NULL;
          char *encoded;

          dispatch_data_t md = dispatch_data_create_map(pipe data,(const void **)&bytes,&len);
          asl_set((aslmsg)merged_msg,ASL_KEY_AUX_DATA,encoded);
          free(encoded);
          _asl_send_message(NULL,merged_msg,-1,NULL);
          asl_msg_release(merged_msg);
          dispatch_release(md);
       }
      }

      if(done)
      {
         dispatch_semaphore_signal(sem);
         dispatch_release(pipe_channel);
         dispatch_release(pipe_q);
      }
});

10. dispatch_barrier_async:

//隔断方法:当前面的写入操作全部完成之后,再执行后面的读取任务。当然也可以用Dispatch Group和dispatch_set_target_queue,只是比较而言,dispatch_barrier_async会更加顺滑
-(void)dispatch_barrier_async{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        // 第一个写入任务
        [NSThread sleepForTimeInterval:3];
        
        NSLog(@"----执行第一个写入任务---当前线程%@",[NSThread currentThread]);
        
    });
    dispatch_async(queue, ^{
        // 第二个写入任务
        [NSThread sleepForTimeInterval:1];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
        
    });
    
    dispatch_barrier_async(queue, ^{
        // 等待处理
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----等待前面的任务完成---当前线程%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        // 第一个读取任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第一个读取任务---当前线程%@",[NSThread currentThread]);
        
    });
    dispatch_async(queue, ^{
        // 第二个读取任务
        [NSThread sleepForTimeInterval:2];
        
        NSLog(@"----执行第二个读取任务---当前线程%@",[NSThread currentThread]);
        
    });
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);

}

11. dispatch_suspend & dispatchp_resume:

//场景:当追加大量处理到Dispatch Queue时,在追加处理的过程中,有时希望不执行已追加的处理。例如演算结果被Block截获时,一些处理会对这个演算结果造成影响。在这种情况下,只要挂起Dispatch Queue即可。当可以执行时再恢复。
//总结:dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的任务,也就是如果挂起之前已经有队列中的任务在进行中,那么该任务依然会被执行完毕
-(void)dispatch_suspend{
    
    NSLog(@"----start-----当前线程---%@",[NSThread currentThread]);

    dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        // 执行第一个任务
        [NSThread sleepForTimeInterval:5];
        
        NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        // 执行第二个任务
        [NSThread sleepForTimeInterval:5];
        
        NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        // 执行第三个任务
        [NSThread sleepForTimeInterval:5];
        
        NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
    });
    
    //此时发现意外情况,挂起队列
    NSLog(@"suspend");
    dispatch_suspend(queue);
    
    //挂起10秒之后,恢复正常
    [NSThread sleepForTimeInterval:10];
    
    //恢复队列
    NSLog(@"resume");
    dispatch_resume(queue);
    
    NSLog(@"----end-----当前线程---%@",[NSThread currentThread]);
    
}

参考资料:

  • Objective-C 高级编程 iOS 与 OS X 多线程和内存管理

gcd_demo's People

Contributors

ktdynamic avatar mrtung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.