标签:
| 
 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 ()@endstatic 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 ()@endstatic 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