GCD多線程下,實現線程同步的方式有如下幾種:
1.串行隊列2.並行隊列3.分組 4.信號量
實例: 去網上獲取一張圖片並展示在視圖上. 實現這個需求,可以拆分成兩個任務,一個是去網上獲取圖片,一個是展示在視圖上. 這兩個任務是有關聯的,所以需要同步處理.
下面看這幾種方式如何實現.
一、
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操作.