深入理解 GCD

1.Dispatch Group

在追加到 Dispatch Queue 中的多個(gè)任務(wù)處理完畢之后想執(zhí)行結(jié)束處理,這種需求會(huì)經(jīng)常出現(xiàn)。如果只是使用一個(gè) Serial Dispatch Queue (串行隊(duì)列)時(shí),只想要將執(zhí)行的處理全部追加到改串行隊(duì)列中并在最后追加結(jié)束處理即可,但是在使用 Concurrent Queue 時(shí),可能會(huì)同時(shí)使用多個(gè) Dispatch Queue 時(shí),源碼就會(huì)變得很復(fù)雜。

這種情況下,就可以使用 Dispatch Group。

  dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t  queue = dispatch_queue_create("com.wangwang.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 1000; i++) {
            if (i == 999) {
                NSLog(@"11111");
            }
        }
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"22222222");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"3333");
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"done");
    });

因?yàn)橄?Concurrent Dispatch Queue 追加處理,多個(gè)線(xiàn)程并行執(zhí)行,所以追加處理的執(zhí)行順序不定。執(zhí)行順序會(huì)發(fā)生變化,但是執(zhí)行結(jié)果的 done 一定是最后輸出的。

無(wú)論向什么樣的 Dispatch Queue 中追加處理,使用 Dispatch Group 都可以監(jiān)視這些處理執(zhí)行的結(jié)果。一旦監(jiān)測(cè)到所有的處理執(zhí)行結(jié)束,就可以將結(jié)束的處理追加到 Dispatch Queue 中,這就是使用 Dispatch Group 的原因。

下面是一個(gè)使用 Dispatch Group 異步下載兩張圖片,然后合并成一張圖片的 demo。

#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(@"在下載圖片的時(shí)候,主線(xiàn)程貌似還可以干點(diǎn)什么");
}
- (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](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(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

2. Dispatch_barrier_async

在訪問(wèn)數(shù)據(jù)庫(kù)或者文件的時(shí)候,我們可以使用Serial Dispatch Queue可避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,代碼如下所示:
其實(shí)使用GCD可以簡(jiǎn)單高效的代替同步塊或者鎖對(duì)象,可以使用,串行同步隊(duì)列,將讀操作以及寫(xiě)操作都安排在同一個(gè)隊(duì)列里,即可保證數(shù)據(jù)同步,代碼如下:

多個(gè)getter方法(也就是讀?。┦强梢圆l(fā)執(zhí)行的,而getter(讀)與setter(寫(xiě))方法是不能并發(fā)執(zhí)行的,利用這個(gè)特點(diǎn),還能寫(xiě)出更快的代碼來(lái),這次注意,不用串行隊(duì)列,而改用并行隊(duì)列:

#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

這樣優(yōu)化,可以發(fā)現(xiàn)這種做法肯定比使用串行隊(duì)列要快。
在這個(gè)代碼中,我用了點(diǎn)新的東西,dispatch_barrier_async,可以翻譯成柵欄(barrier),它可以往隊(duì)列里面發(fā)送任務(wù)(塊,也就是block),這個(gè)任務(wù)有柵欄(barrier)的作用。
在隊(duì)列中,barrier塊必須單獨(dú)執(zhí)行,不能與其他block并行。這只對(duì)并發(fā)隊(duì)列有意義,并發(fā)隊(duì)列如果發(fā)現(xiàn)接下來(lái)要執(zhí)行的block是個(gè)barrier block,那么就一直要等到當(dāng)前所有并發(fā)的block都執(zhí)行完畢,才會(huì)單獨(dú)執(zhí)行這個(gè)barrier block代碼塊,等到這個(gè)barrier block執(zhí)行完畢,再繼續(xù)正常處理其他并發(fā)block。在上面的代碼中,setter方法中使用了barrier block以后,對(duì)象的讀取操作依然是可以并發(fā)執(zhí)行的,但是寫(xiě)入操作就必須單獨(dú)執(zhí)行了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我們知道在iOS開(kāi)發(fā)中,一共有四種多線(xiàn)程技術(shù):pthread,NSThread,GCD,NSOperation: ...
    請(qǐng)叫我周小帥閱讀 1,559評(píng)論 0 1
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時(shí)候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似。...
    Dely閱讀 9,401評(píng)論 21 42
  • 蜂蜜從來(lái)不會(huì)挑剔愛(ài)人的身材,不管你是勻稱(chēng)緊實(shí)的檸檬還是下肢肥胖的雪梨或全身虛胖的柚子,它都一樣是你最甜蜜的陪伴。 ...
    小凱兔崽子閱讀 257評(píng)論 0 0
  • NSObject.mm源碼 對(duì)象--id arm64 架構(gòu)中的 isa_t 結(jié)構(gòu)體 (bits格式一樣,一些信息的...
    啊哈呵閱讀 1,339評(píng)論 0 13
  • 總有一段往事會(huì)出現(xiàn)在夢(mèng)中,以曾經(jīng)想過(guò)的最美好的狀態(tài),比如和第一個(gè)愛(ài)的人在一起,比如實(shí)現(xiàn)曾經(jīng)最想要的幸福。 年少懵懂...
    赤苼閱讀 524評(píng)論 0 1

友情鏈接更多精彩內(nèi)容