标签:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create( "com.gcd-group.www" , DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, queue, ^{ for ( int i = 0; i < 1000; i++) { if (i == 999) { NSLog (@ "11111111" ); } } }); dispatch_group_async(group, queue, ^{ NSLog (@ "22222222" ); }); dispatch_group_async(group, queue, ^{ NSLog (@ "33333333" ); }); dispatch_group_notify(group, queue, ^{ NSLog (@ "done" ); }); |
控制台的输出:
因为向Concurrent Dispatch Queue 追加处理,多个线程并行执行,所以追加处理的执行顺序不定。执行顺序会发生变化,但是此执行结果的done一定是最后输出的。
无论向什么样的Dispatch Queue中追加处理,使用Dispatch Group都可以监视这些处理执行的结果。一旦检测到所有处理执行结束,就可以将结束的处理追加到Dispatch Queue中,这就是使用Dispatch Group的原因。
下面试一个使用Dispatch Group异步下载两张图片,然后合并成一张图片的medo(注意,我们总是应该在主线程中更新UI):
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
#import "ViewController.h" @interface ViewController () @property ( nonatomic , strong) UIImage *imageOne; @property ( nonatomic , strong) UIImage *imageTwo; @property ( nonatomic , weak) UILabel *textLabel; @end @implementation ViewController - ( void )viewDidLoad { [ super viewDidLoad]; [ self operation1]; } - ( void )operation1 { UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(200, 450, 0, 0)]; textLabel.text = @ "正在下载图片" ; [textLabel sizeToFit]; [ self .view addSubview:textLabel]; self .textLabel = textLabel; [ self group]; NSLog (@ "在下载图片的时候,主线程貌似还可以干点什么" ); } - ( void )group { UIImageView *imageView = [[UIImageView alloc] init]; [ self .view addSubview:imageView]; dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create( "cn.gcd-group.www" , DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, queue, ^{ NSLog (@ "正在下载第一张图片" ); NSData *data = [ NSData dataWithContentsOfURL:[ NSURL URLWithString:@ "http://images2015.cnblogs.com/blog/471463/201509/471463-20150912213125372-589808688.png" ]]; NSLog (@ "第一张图片下载完毕" ); self .imageOne = [UIImage imageWithData:data]; }); dispatch_group_async(group, queue, ^{ NSLog (@ "正在下载第二张图片" ); NSData *data = [ NSData dataWithContentsOfURL:[ NSURL URLWithString:@ "http://images2015.cnblogs.com/blog/471463/201509/471463-20150912212457684-585830854.png" ]]; NSLog (@ "第二张图片下载完毕" ); self .imageTwo = [UIImage imageWithData:data]; }); dispatch_group_notify(group, queue, ^{ UIGraphicsBeginImageContext(CGSizeMake(300, 400)); [ self .imageOne drawInRect:CGRectMake(0, 0, 150, 400)]; [ self .imageTwo drawInRect:CGRectMake(150, 0, 150, 400)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); dispatch_async(dispatch_get_main_queue(), ^{ UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage]; [ self .view addSubview:imageView]; self .textLabel.text = @ "图片合并完毕" ; }); }); } @end |
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
|
#import <Foundation/Foundation.h> @interface ZYPerson : NSObject @property ( nonatomic , copy ) NSString *name; @end #import "ZYPerson.h" static NSString *_name; @implementation ZYPerson - ( void )setName:( NSString *)name { @synchronized ( self ) { _name = [name copy ]; } } - ( NSString *)name { @synchronized ( self ) { return _name; } } @end |
这是我在刚学iOS开发,刚涉及并发中的数据竞争时,书本上提到的一种解决方案。如果有多个线程要执行同一份代码,那么有时候可能会出现问题,这种情况下,通常要使用锁来实现某种同步机制。iOS提供了一种加锁的方式,就是采用内置的synchronization block,也就是上面代码所写的。
这种写法会根据给定的对象,自动创建一个锁,并等待块中的代码执行完毕。执行到这段代码结尾处,锁也就释放了。在上面的例子中,同步行为所针对的对象是self。这么写通常没错,但是@synchronized(self)会大大降低代码效率,甚至很多时候,还可以被人感觉到效率明显下降了,因为共用同一个锁的那些同步块,都必须按顺序执行。若在self对象上频繁加锁,那么程序可能就要等另一段与此无关的代码执行完毕,才可以继续执行当前代码,这样做是很没必要的。
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
35
36
37
38
39
|
#import <Foundation/Foundation.h> @interface ZYPerson : NSObject @property ( nonatomic , copy ) NSString *name; @end #import "ZYPerson.h" @interface ZYPerson () @end static NSString *_name; static dispatch_queue_t _queue; @implementation ZYPerson - (instancetype)init { if ( self = [ super init]) { _queue = dispatch_queue_create( "com.person.syncQueue" , DISPATCH_QUEUE_SERIAL); } return self ; } - ( void )setName:( NSString *)name { dispatch_sync(_queue, ^{ _name = [name copy ]; }); } - ( NSString *)name { __block NSString *tempName; dispatch_sync(_queue, ^{ tempName = _name; }); return tempName; } @end |
这样写的思路是:把写操作与读操作都安排在同一个同步串行队列里面执行,这样的话,所有针对属性的访问操作就都同步了。
这种方法的确已经足够好了,但还不是最优的,它只可以实现单读、单写。整体来看,我们最终要解决的问题是,在写的过程中不能被读,以免数据不对,但是读与读之间并没有任何的冲突!
多个getter方法(也就是读取)是可以并发执行的,而getter(读)与setter(写)方法是不能并发执行的,利用这个特点,还能写出更快的代码来,这次注意,不用串行队列,而改用并行队列:
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
35
36
37
|
#import <Foundation/Foundation.h> @interface ZYPerson : NSObject @property ( nonatomic , copy ) NSString *name; @end #import "ZYPerson.h" @interface ZYPerson () @end static NSString *_name; static dispatch_queue_t _concurrentQueue; @implementation ZYPerson - (instancetype)init { if ( self = [ super init]) { _concurrentQueue = dispatch_queue_create( "com.person.syncQueue" , DISPATCH_QUEUE_CONCURRENT); } return self ; } - ( void )setName:( NSString *)name { dispatch_barrier_async(_concurrentQueue, ^{ _name = [name copy ]; }); } - ( NSString *)name { __block NSString *tempName; dispatch_sync(_concurrentQueue, ^{ tempName = _name; }); return tempName; } @end |
这样优化,测试一下性能,可以发现这种做法肯定比使用串行队列要快。
在这个代码中,我用了点新的东西,dispatch_barrier_async,可以翻译成栅栏(barrier),它可以往队列里面发送任务(块,也就是block),这个任务有栅栏(barrier)的作用。
在队列中,barrier块必须单独执行,不能与其他block并行。这只对并发队列有意义,并发队列如果发现接下来要执行的block是个barrier block,那么就一直要等到当前所有并发的block都执行完毕,才会单独执行这个barrier block代码块,等到这个barrier block执行完毕,再继续正常处理其他并发block。在上面的代码中,setter方法中使用了barrier block以后,对象的读取操作依然是可以并发执行的,但是写入操作就必须单独执行了。
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
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ NSLog (@ "11 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ NSLog (@ "22 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ NSLog (@ "33 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ NSLog (@ "44 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ NSLog (@ "55 %@" ,[ NSThread currentThread]); }); }); |
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
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog (@ "11 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog (@ "22 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog (@ "33 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog (@ "44 %@" ,[ NSThread currentThread]); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog (@ "55 %@" ,[ NSThread currentThread]); }); }); |
iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写)
标签:
原文地址:http://www.cnblogs.com/VzhanggengD/p/5499371.html