GCD的基本使用

同步

需要等待Block或者闭包代码块执行完毕才继续执行下一步操作。也就是说这里是一个线程阻塞。阻塞当前线程,添加任务到queue中,等待queue队列执行该任务。因此同步稍不注意就会死锁。

1
2
3
4
void dispatch_sync (
dispatch_queue_t queue,
dispatch_block_t block
);

异步

Block代码块任务加入到queue队列中,然后继续执行下一步操作。

1
2
3
4
void dispatch_async (
dispatch_queue_t queue,
dispatch_block_t block
);

主队列

主队列是全局唯一的,是串行队列。

同步

1
2
3
dispatch_sync(dispatch_get_main_queue(), ^{
// do something task
});

异步

1
2
3
dispatch_async(dispatch_get_main_queue(), ^{
// do something task
});

全局队列

全局队列是并行队列。

获取全局队列API

1
2
3
4
dispatch_queue_t dispatch_get_global_queue (
long identifier,
unsigned long flags
);

identifier

队列执行任务的服务质量,服务质量有助于确定执行任务的优先级,可以指定以下QOS枚举值。

  • QOS_CLASS_USER_INTERACTIVE
  • QOS_CLASS_USER_INITIATED
  • QOS_CLASS_DEFAULT
  • QOS_CLASS_UTILITY
  • QOS_CLASS_BACKGROUND

用于处理用户交互或者用户发起的任务队列意味着优先级会比后台运行的任务要高,你也可以手动指定队列的优先级值。dispatch_queue_priority_t这实际是就是一个long类型值。

而我们使用的时候都不会使用这个,而是直接使用宏,即如下几个值。

  • DISPATCH_QUEUE_PRIORITY_HIGH
  • DISPATCH_QUEUE_PRIORITY_DEFAULT
  • DISPATCH_QUEUE_PRIORITY_LOW
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND

实际上他们是相互映射的。

宏定义 QOS
DISPATCH_QUEUE_PRIORITY_HIGH QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND QOS_CLASS_BACKGROUND

flags

保留参数,以供将来使用,现在始终是0

获取全局队列

1
2
3
4
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)

使用全局队列

只需要将使用相应的优先级就尅了。

1
2
3
4
5
6
7
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// do something task.
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// do something task.
});

如何创建自定义的队列

创建队列的API

1
2
3
4
dispatch_queue_t dispatch_queue_create (
const char *label
dispatch_queue_attr_t attr
);

label

这是一个C语言字符串,而不是Objective-C中的NSString。所以直接使用""就可以了。这里一般使用Bundle Identifier 加上有业务含义的名字,如何db,networking,用以处理数据库,网络的队列。这样会出现在app崩溃的时所产生的CrashLog中,而且调试的时候也能够查看到是哪一个队列。

attr

虽然可以直接赋值为NULL,表示串行队列,但是为了更容易理解,强烈建议使用官方已经自定义好的宏。如下,

  • DISPATCH_QUEUE_SERIAL 串行
  • DISPATCH_QUEUE_CONCURRENT 并行

虽然DISPATCH_QUEUE_SERIAL实际上还是一个NULL, 但还是强烈推荐使用DISPATCH_QUEUE_SERIAL。

创建串行队列

1
dispatch_queue_t queue = dispatch_queue_create("com.lucaslz.db", DISPATCH_QUEUE_SERIAL);

使用自定义的串行队列

1
2
3
4
5
6
7
8
9
10
11
dispatch_queue_t queue = dispatch_queue_create("com.lucaslz.db", DISPATCH_QUEUE_SERIAL);

// 同步
dispatch_async(queue, ^{
// do something task.
});

// 异步
dispatch_sync(queue, ^{
// do something task.
});

创建并行队列

1
dispatch_queue_t queue = dispatch_queue_create("com.lucaslz.db", DISPATCH_QUEUE_CONCURRENT);

使用自定义的并行队列

1
2
3
4
5
6
7
8
9
10
11
dispatch_queue_t queue = dispatch_queue_create("com.lucaslz.db", DISPATCH_QUEUE_CONCURRENT);

// 同步
dispatch_async(queue, ^{
// do something task.
});

// 异步
dispatch_sync(queue, ^{
// do something task.
});

串行队列

串行同步

创建串行队列,并添加同步任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)viewDidLoad {
[super viewDidLoad];

NSLog(@"task start, thread = %@", [NSThread currentThread]);

dispatch_queue_t serialQueue = dispatch_queue_create("com.lucaslz.db", DISPATCH_QUEUE_SERIAL);
for (NSUInteger index = 1; index <= 10; ++index ) {
dispatch_sync(serialQueue, ^{
NSLog(@"task = %@ thread = %@", @(index), [NSThread currentThread]);
});
}

NSLog(@"task end, thread = %@", [NSThread currentThread]);
}

控制台输出结果

1
2
3
4
5
6
7
8
9
10
11
12
task start, thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 1 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 2 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 3 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 4 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 5 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 6 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 7 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 8 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 9 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task = 10 thread = <NSThread: 0x7fd760702590>{number = 1, name = main}
task end, thread = <NSThread: 0x7fd760702590>{number = 1, name = main}

上下文环境是在viewDidLoad中,所以实际上是在主线程去执行串行异步任务。

  • 先执行task start
  • 然后执行for循环, 把任务添加进串行队列中,因为这是一个同步任务,所以会等待所有的同步任务执行完毕。
  • 再执行task end

从输出结果可以看出,串行同步并不会创建新的线程,实际上还是在主线程中执行任务。

串行异步

把刚刚的串行同步换成串行异步

1
2
3
dispatch_async(serialQueue, ^{
NSLog(@"task = %@ thread = %@", @(index), [NSThread currentThread]);
});

控制台输出结果

1
2
3
4
5
6
7
8
9
10
11
12
task start, thread = <NSThread: 0x7f83c2504270>{number = 1, name = main}
task end, thread = <NSThread: 0x7f83c2504270>{number = 1, name = main}
task = 1 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 2 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 3 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 4 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 5 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 6 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 7 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 8 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 9 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}
task = 10 thread = <NSThread: 0x7f83c2416ed0>{number = 2, name = (null)}

上下文环境是在viewDidLoad中,所以实际上是在主线程去执行串行异步任务。

  • 先执行Task start。
  • 然后执行for循环, 把任务添加进串行队列中,因为是异步任务,所以不会等待。
  • 继续执行task end任务。
  • 再执行串行队列中的所有任务。

串行异步,从输出结果中可以看到,会创建线程(number = 2, name = (null))。
number = 1, name= main是主线程。

并行队列

并行同步

创建并行队列concurrentQueue。并调用同步任务。

1
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.lucaslz.db", DISPATCH_QUEUE_CONCURRENT);

控制台输出结果

1
2
3
4
5
6
7
8
9
10
11
12
task start, thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 1 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 2 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 3 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 4 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 5 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 6 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 7 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 8 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 9 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task = 10 thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}
task end, thread = <NSThread: 0x7fe1725077c0>{number = 1, name = main}

  • 先执行task start
  • 再执行for循环,并把同步任务添加到并行队列中,因为是同步的~ 所以依旧就等待同步任务完成
  • 等所有的同步任务完成后才会去执行 task end

从输出结果中可以看出,并行同步任务,也没有创建线程,与串行同步任务一样都是使用的主线程。

并行异步

把同步任务替换成异步任务

1
2
3
dispatch_async(concurrentQueue, ^{
NSLog(@"task = %@ thread = %@", @(index), [NSThread currentThread]);
});

控制台输出结果

1
2
3
4
5
6
7
8
9
10
11
12
task start, thread = <NSThread: 0x7fda2b504220>{number = 1, name = main}
task end, thread = <NSThread: 0x7fda2b504220>{number = 1, name = main}
task = 2 thread = <NSThread: 0x7fda2b419680>{number = 3, name = (null)}
task = 1 thread = <NSThread: 0x7fda2b71de60>{number = 2, name = (null)}
task = 3 thread = <NSThread: 0x7fda2b506970>{number = 4, name = (null)}
task = 4 thread = <NSThread: 0x7fda2b50db50>{number = 5, name = (null)}
task = 5 thread = <NSThread: 0x7fda2b419680>{number = 3, name = (null)}
task = 6 thread = <NSThread: 0x7fda2b7034e0>{number = 6, name = (null)}
task = 7 thread = <NSThread: 0x7fda2b71de60>{number = 2, name = (null)}
task = 8 thread = <NSThread: 0x7fda2b72f9b0>{number = 7, name = (null)}
task = 9 thread = <NSThread: 0x7fda2b506970>{number = 4, name = (null)}
task = 10 thread = <NSThread: 0x7fda2b50db50>{number = 5, name = (null)}

上下文环境是在viewDidLoad中,所以实际上是在主线程去执行并行异步任务。

  • 先执行task start任务
  • 再执行for循环,并把异步任务添加到并行队列中,因为是异步任务,所以不需要等待,继续往下执行任务
  • 执行task end
  • 再执行并行队列中的任务

从输出结果中可以看出,并行异步任务,会创建多个线程,创建的线程数量会根据系统资源而定。

全局队列

全局队列同步

创建默认优先级的全局队列,并添加同步任务。

1
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

控制台输出结果

1
2
3
4
5
6
7
8
9
10
11
12
task start, thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 1 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 2 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 3 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 4 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 5 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 6 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 7 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 8 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 9 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task = 10 thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}
task end, thread = <NSThread: 0x7fc390701d10>{number = 1, name = main}

可以看出与并行同步执行的结果一样。

全局队列异步

把同步任务修改成异步任务

1
2
3
dispatch_async(defaultQueue, ^{
NSLog(@"task = %@ thread = %@", @(index), [NSThread currentThread]);
});

控制台输出结果

1
2
3
4
5
6
7
8
9
10
11
12
task start, thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 1 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 2 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 3 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 4 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 5 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 6 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 7 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 8 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 9 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task = 10 thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}
task end, thread = <NSThread: 0x7fe578602e30>{number = 1, name = main}

可以看出与并行异步队列执行的结果是一样。

其他的优先级全局队列

1
2
3
4
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

结果同上面的默认优先级的全局队列。

主队列

主队列同步

同样的把队列修改成主队列。

1
dispatch_queue_t mainQueue = dispatch_get_main_queue();

控制台输出结果

1
task start, thread = <NSThread: 0x7fe5c9d1aaf0>{number = 1, name = main}

等待了很长一段时间,你发现同步任务没有执行。
实际上这已经是一个死锁了。

因为上下文环境是在viewDidLoad中,所以实际上本身就是在主队列中。
先执行task start。然后把同步任务添加到主队列中,因为是同步任务,所以需要等待同步任务执行。而因为队列是先进先出FIFO的,即刚刚添加的任务需要等待前面一个任务执行完才能执行,而这又是一个同步任务,需要等这个任务执行,这个任务又需要前面的任务执行,所以造成了互相等待,结局就是死锁。

主队列异步

把同步任务修改成异步任务。

控制台输出结果

1
2
3
4
5
6
7
8
9
10
11
12
task start, thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task end, thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 1 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 2 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 3 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 4 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 5 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 6 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 7 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 8 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 9 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}
task = 10 thread = <NSThread: 0x7fd4c860f7f0>{number = 1, name = main}

可以看出跟串行异步是一样的结果。

主队列、全局队列、自定义队列差异

上下文环境是在viewDidLoad中。(这个也很重要,不然可能导致结论不一致)

队列 队列类型 任务类型 创建新线程 执行顺序
Main 串行 sync NO 死锁
Main 串行 async NO 顺序执行
GlobalQueue 并行 sync NO 顺序执行
GlobalQueue 并行 async YES(多个线程) 没有先后顺序
自定义 串行 sync NO 顺序执行
自定义 串行 async YES (只有一个) 顺序执行
自定义 并行 sync NO 顺序执行
自定义 并行 async YES (多个线程) 没有先后顺序
  • 并行、串行同步任务都不会创建新的线程,只会在主线程中执行任务。
  • 只有并发异步任务才会创建多个线程。
坚持原创技术分享,您的支持将鼓励我继续创作!