iOS 多线程编程 GCD

GCD,全名是 Grand Central Dispatch ,通常是 create 一个 dispatch_queue_t 队列,然后把任务放到队列中,队列分为 dispatch_async (异步队列)和 dispatch_sync (同步队列)。

  • 串行队列
  • 并行队列
  • 全局队列
  • 主线程队列
  • 实际操作一(网络下载图片)
  • 实际操作二(iOS 推送并发执行)

简单地理解

  • GCD 的基本思想是就将操作放在队列中去执行
  • 操作使用 Block 定义
  • 队列负责调度任务执行所在的线程以及具体的执行时间
  • 队列的特点是先进先出(FIFO)的,新添加至对列的操作都会排在队尾
  • GCD 的函数都是以 dispatch 开头的

串行队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void)gcdDemo1 {

// 串行队列
dispatch_queue_t q = dispatch_queue_create("com.pupboss.gcddemo1", DISPATCH_QUEUE_SERIAL);

// 串行行队列的同步任务,同样会在主线程上运行
for (int i = 0; i < 5; ++i) {
// 同步任务顺序执行
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}

for (int i = 0; i < 5; ++i) {
// 异步任务,并发执行,但是如果在串行队列中,仍然会依次顺序执行
dispatch_async(q, ^{
// [NSThread currentThread] 可以在开发中,跟踪当前线程
// num = 1,表示主线程
// num = 2,表示第2个子线程。。。
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
}

2015-05-23 22:03:17.990 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 0
2015-05-23 22:03:17.990 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 1
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 2
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 3
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 4
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 0
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 1
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 2
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 3
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 4

并行队列

这个比较有意思了,看下面这种情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)gcdDemo2 {
// 并行队列,执行顺序不确定,线程个数不确定
dispatch_queue_t q = dispatch_queue_create("com.pupboss.gcddemo2", DISPATCH_QUEUE_CONCURRENT);

for (int i = 0; i < 5; ++i) {
// 异步任务
dispatch_async(q, ^{
// [NSThread currentThread] 可以在开发中,跟踪当前线程
// num = 1,表示主线程
// num = 2,表示第2个子线程。。。
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}

for (int i = 0; i < 5; ++i) {
// 同步任务顺序执行
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
}

执行结果是下面的,非常混乱,是吧,但是

1
2
3
4
5
6
7
8
9
10
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212722] <NSThread: 0x7f98e9600000>{number = 5, name = (null)} 3  
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212715] <NSThread: 0x7f98e9979e90>{number = 2, name = (null)} 0
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 0
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212714] <NSThread: 0x7f98e9a00a70>{number = 4, name = (null)} 1
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212716] <NSThread: 0x7f98e98301d0>{number = 3, name = (null)} 2
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212723] <NSThread: 0x7f98e9a003b0>{number = 6, name = (null)} 4
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 1
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 2
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 3
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 4

如果把两个任务换换位置,变成这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (int i = 0; i < 5; ++i) {
// 同步任务顺序执行
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}

for (int i = 0; i < 5; ++i) {
// 异步任务
dispatch_async(q, ^{
// [NSThread currentThread] 可以在开发中,跟踪当前线程
// num = 1,表示主线程
// num = 2,表示第2个子线程。。。
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}

运行结果就变了

1
2
3
4
5
6
7
8
9
10
2015-05-23 22:04:58.874 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 0  
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 1
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 2
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 3
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 4
2015-05-23 22:04:58.880 Multi-Thread-Test[4555:214043] <NSThread: 0x7fc8d3e00ae0>{number = 2, name = (null)} 0
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214043] <NSThread: 0x7fc8d3e00ae0>{number = 2, name = (null)} 1
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214044] <NSThread: 0x7fc8d3e03280>{number = 3, name = (null)} 2
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214043] <NSThread: 0x7fc8d3e00ae0>{number = 2, name = (null)} 4
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214046] <NSThread: 0x7fc8d3f0adb0>{number = 4, name = (null)} 3

说明并行队列中的同步任务,还是在主线程上,并且执行完了之后才会往下走

全局队列

苹果为了方便多线程的设计,提供一个全局队列,供所有的APP共同使用

1
dispatch_queue_t  queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

两个参数,一个是优先级,一个是 0,现在只能写 0,因为苹果没想好这个参数怎么开放给开发者。

全局队列用起来跟并行队列是差不多的,所以就不上代码了

主线程队列

每一个应用程序都只有一个主线程,有些操作,需要放到主线程队列来完成,比如数据下载完,刷新界面,等等

主线程队列千万不要使用同步任务,会发生阻塞!

1
2
3
4
5
6
7
8
9
10
11
- (void)gcdDemo4 {

dispatch_queue_t q = dispatch_get_main_queue();

// 异步任务,在主线程上运行,同时是保持队形的
for (int i = 0; i < 5; ++i) {
dispatch_async(q, ^{
NSLog(@"%@ - %d", [NSThread currentThread], i);
});
}
}

运行结果

1
2
3
4
5
2015-05-23 22:34:17.663 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 0  
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 1
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 2
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 3
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 4

批量下载图片

1
2
3
4
5
6
7
8
9
10
11
12
for (int i = 1; i < 100; ++i) {

dispatch_sync(q, ^{

NSLog(@"%@ %d", [NSThread currentThread], i);
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", i];

NSString *url = [NSString stringWithFormat:@"http://xxxxy/%@", imageName];

[LJFileTool writeImageToFileName:imageName withImgURL:url];
});
}

这么写没什么好处 = =,也就是不阻塞主线程,除此之外真没啥了。。。。卧槽,本来想写成异步任务的,结果发现老是有问题,而且速度也一般。

iOS 的推送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSArray *startArr = [NSArray arrayWithContentsOfFile:@"/xxxxx/test.plist"];

dispatch_queue_t q = dispatch_queue_create("com.pupboss.pusher", DISPATCH_QUEUE_SERIAL);

for (int i = 0; i < startArr.count; ++i) {
dispatch_async(q, ^{

NSLog(@"%@ %d", [NSThread currentThread], i);

NSLog(@"%@", startArr[i]);

[self sendMsg:startArr[i]];
});
}
文章目录
  1. 1. 简单地理解
  2. 2. 串行队列
  3. 3. 并行队列
  4. 4. 全局队列
  5. 5. 主线程队列
  6. 6. 批量下载图片
  7. 7. iOS 的推送