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操作.
二、
以上幾種方式,都是通過阻塞線程的方式去實現同步。