GCD多線程下,實現線程同步的方式有以下幾種:
1.串行隊列 2.並行隊列 3.分組 4.信號量
實例: 去網上獲取一張圖片並展現在視圖上. 實現這個需求,能夠拆分紅兩個任務,一個是去網上獲取圖片,一個是展現在視圖上. 這兩個任務是有關聯的,因此須要同步處理.
下面看這幾種方式如何實現.
1、
1.串行隊列
1.1[GCD相關:]
(1)GCD下的dispatch_queue隊列都是FIFO隊列,都會按照提交到隊列的順序執行.
只是根據隊列的性質,分爲<1>串行隊列:用戶隊列、主線程隊列 <2>並行隊列.
(2)同步(dispatch_sync)、異步方式(dispatch_async). 配合串行隊列和並行隊列使用.
1.2同步隊列直接提交兩個任務就能夠.
// 串形隊列
dispatch_queue_t serilQueue = dispatch_queue_create("com.quains.myQueue", 0);
//開始時間
NSDate *startTime = [NSDate date];
__block UIImage *image = nil;
//1.先去網上下載圖片
dispatch_async(serilQueue, ^{
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [[UIImage imageWithData:imageData] retain];
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
});
//2.在主線程展現到界面裏
dispatch_async(serilQueue, ^{
NSLog(@"%@",[NSThread currentThread]);
// 在主線程展現
dispatch_async(dispatch_get_main_queue(), ^{
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"串行異步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
});
});
//3.清理
dispatch_release(serilQueue);
[image release];
注意:
(1) __block變量分配在棧,retain下,防止被回收.
(2)dispatch要手動create和release.
(3)提交到主線程隊列的時候,慎用同步dispatch_sync方法,有可能形成死鎖. 由於主線程隊列是串行隊列,要等隊列裏的任務一個一個執行.因此提交一個任務到隊列,若是用同步方法就會阻塞住主線程,而主線程又要等主線程隊列裏的任務都執行完才能執行那個剛提交的,因此主線程隊列裏還有其餘的任務的話,但他已經被阻塞住了,無法先完成隊列裏的其餘任務,即,最後一個任務也沒機會執行到,因而形成死鎖.
(4)提交到串行隊列能夠用同步方式,也能夠用異步方式.
2.並行隊列
採用並行隊列的時候,能夠採用同步的方式把任務提交到隊列裏去,便可以實現同步的方式
//新建一個隊列
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//記時
NSDate *startTime = [NSDate date];
//加入隊列
dispatch_async(concurrentQueue, ^{
__block UIImage *image = nil;
//1.先去網上下載圖片
dispatch_sync(concurrentQueue, ^{
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [UIImage imageWithData:imageData];
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
});
//2.在主線程展現到界面裏
dispatch_sync(dispatch_get_main_queue(), ^{
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"並行同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
});
});
兩個同步的任務用一個異步的包起來,提交到並行隊列裏去,便可實現同步的方式.
3.使用分組方式
3.1 group自己是將幾個有關聯的任務組合起來,而後提供給開發者一個知道這個group結束的點.
雖然這個只有一個任務,可是能夠利用group的結束點,去阻塞線程,從而來實現同步方式.
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSDate *startTime = [NSDate date];
__block UIImage *image = nil;
dispatch_group_async(group, queue, ^{
//1.先去網上下載圖片
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [[UIImage imageWithData:imageData] retain];
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
});
// 2.等下載好了再在刷新主線程
dispatch_group_notify(group, queue, ^{
//在主線程展現到界面裏
dispatch_async(dispatch_get_main_queue(), ^{
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
[image release];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"分組同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
});
});
// 釋放掉
dispatch_release(group);
dispatch_group 也要手動建立和釋放.
dispatch_notify()提供了一個知道group何時結束的點. 固然也可使用dispatch_wait()去阻塞.
4.信號量
信號量 和 瑣 的做用差很少,能夠用來實現同步的方式.
可是信號量一般用在 容許幾個線程同時訪問一個資源,經過信號量來控制訪問的線程個數.
// 信號量初始化爲1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSDate *startTime = [NSDate date];
__block UIImage *image = nil;
//1.先去網上下載圖片
dispatch_async(queue, ^{
// wait操做-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 開始下載
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [[UIImage imageWithData:imageData] retain];
//NSLog(@"heap %@", image);
//NSLog(@"%d",[image retainCount]);
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
// signal操做+1
dispatch_semaphore_signal(semaphore);
});
// 2.等下載好了再在刷新主線程
dispatch_async(dispatch_get_main_queue(), ^{
// wait操做-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
NSLog(@"%d",[image retainCount]);
[image release];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"信號量同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
// signal操做+1
dispatch_semaphore_signal(semaphore);
});
dispatch_wait會阻塞線程而且檢測信號量的值,直到信號量值大於0纔會開始往下執行,同時對信號量執行-1操做.
dispatch_signal則是+1操做.