iOS多线程之GCD
多线程概念
一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。iOS中的程序启动,创建好一个进程的同时, 一个线程便开始运行,这个线程叫主线程。
iOS中的多线程
1.NSThread
2.NSOperation
3.GCD
我自己比较常用GCD,所以今天就主要介绍一下GCD的使用方法
什么是GCD?
Grand Central Dispatch(GCD) 是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。
GCD的优点
优点:
1.不需要关心线程.管理,数据同步的事情。GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
2.GCD主要与block结合使用。代码简洁高效
3.GCD会自动利用更多的CPU内核(比如双核、四核)
了解线程、任务、队列
线程:程序执行任务的最小调度单位
任务:说白了就是一段代码,在GCD中,任务就是block中要执行的内容
队列:用来存放“任务”的一个容器、数组
任务:
执行任务有两种方式:同步执行和异步执行。
两者的主要区别是:是否具备开启新线程的能力。
同步执行(调度)(sync):只能在当前线程中执行任务,不具备开启新线程的能力,任务要创建后执行完才能继续往下走。
异步执行(调度)(async):可以在新的线程中执行任务,具备开启新线程的能力,任务创建后可以绕过,回头在执行。
队列:
这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列和并行队列。
并行队列(Concurrent Dispatch Queue):可以让多个任务并行(同时)执行(自动开启多个线程同时执行任务)
串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
一条重要的准则
一般来说,我们使用GCD的最大目的是在新的线程中同时执行多个任务,这意味着我们需要两项条件:
1.能开启新的线程
2.任务可以同时执行
结合以上两个条件,也就等价“开启新线程的能力 + 任务同步执行的权利”,只有在满足能力与权利这两个条件的前提下,我们才可以在同时执行多个任务。
GCD使用
1.创建一个队列(串行队列或并行队列)
2.将任务添加到队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)
1.创建队列
- 可以使用dispatch_queue_create来创建对象,需要传入两个参数。
第一个参数表示队列的唯一标识符,用于DEBUG,可为空;
第二个参数用来识别是串行队列还是并行队列。DISPATCH_QUEUE_SERIAL表示串行队列。DISPATCH_QUEUE_CONCURRENT表示并行队列。
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("GCDTest.queue", DISPATCH_QUEUE_SERIAL);
// 并行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("GCDTest.queue", DISPATCH_QUEUE_CONCURRENT);
- 对于并行队列,还可以使用dispatch_get_global_queue来创建全局并行队列。
GCD默认提供了全局的并行队列,需要传入两个参数。
第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。
第二个参数暂时没用,用0即可。
2.创建任务
// 同步执行任务创建方法
dispatch_sync(queue, ^{
// 这里放要执行的任务代码
NSLog(@"%@",[NSThread currentThread]);
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
// 这里放要执行的任务代码
NSLog(@"%@",[NSThread currentThread]);
});
-
GCD使用方法很简单,但是队列和任务组合起来使用就有很多种自合方式了,下面是同步、异步、并行、串行 还有 主队列 几个的组合方式
GCD.png
1.异步+并行
- (void)asyncConcurrent {
NSLog(@"异步+并发 ————> begin");
dispatch_queue_t queue= dispatch_queue_create("GCDTest.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"异步+并发 ————> end");
}
2.异步+串行
- (void)asyncSerial {
NSLog(@"异步+串行 ---> begin");
dispatch_queue_t queue = dispatch_queue_create("GCDTest.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"异步+串行 ---> end");
}
3.异步+主队列
- (void)asyncMain {
NSLog(@"主队列 ---> begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"主队列+异步 ---> end");
}
4.同步+并行
- (void)syncConcurrent {
NSLog(@"同步+并发 ---> begin");
dispatch_queue_t queue= dispatch_queue_create("GCDTest.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"同步+并发 ---> end");
}
5.同步+串行
- (void)syncSerial {
NSLog(@"同步+串行 ---> begin");
dispatch_queue_t queue = dispatch_queue_create("GCDTest.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"异步+串行 ---> end");
}
6.同步+主队列
- (void)syncMain {
NSLog(@"主队列+同步 ---> begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"主队列+同步 ---> end");
}
GCD线程之间的通讯
在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。
- (void)gcdConnect {
// 创建全局并发队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 进行耗时的操作
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
// 回到主线程(可以刷新UI等)
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2-------%@",[NSThread currentThread]);
});
});
}
GCD延时方法
- (void)afterTime {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
NSLog(@"2秒后执行的我!!!!!");
});
}
GCD一次性方法
- (void)onceTime {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
NSLog(@"我只被执行了一次!!!!");
});
}
GCD的队列组
有时候我们会有这样的需求:分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的队列组。
我们可以先把任务放到队列中,然后将队列放入队列组中。
调用队列组的dispatch_group_notify回到主线程执行操作。
- (void)gcdGroup {
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
NSLog(@"1.执行一个耗时的操作!!");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
NSLog(@"2.执行一个耗时的操作!!!");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
NSLog(@"耗时的操作完成了!我又回到了主线程");
});
}
这里就介绍了GCD的基本的使用方法,更深层的需要继续学习下去。
欢迎大家的指导!感谢!!