1. 程式人生 > >GCD下的幾種實現同步的方式

GCD下的幾種實現同步的方式

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操作.

二、

以上幾種方式,都是通過阻塞執行緒的方式去實現同步。

相關推薦

GCD實現同步方式

GCD多執行緒下,實現執行緒同步的方式有如下幾種: 1.序列佇列 2.並行佇列 3.分組 4.訊號量 例項: 去網上獲取一張圖片並展示在檢視上. 實現這個需求,可以拆分成兩個任務,一個是去網上獲取圖片,一個是展示在檢視上. 這兩個任務是有關聯的,所以需要同步處理. 下面看這幾種方式如何實現. 一、

Redhat_CentOS軟體安裝方式

 1.rpm RPM  RedHat Package Manager(RedHat軟體包管理工具)的縮寫,這一檔案格式名稱雖然打上了RedHat的標誌,  但是其原始設計理念是開放式的,現在包括OpenLinux、S.u.S.E.以及Turbo Linux等Linux的

linuxIPC通行方式比較

linux上面的IPC大多都是從UNIX上面繼承而來。         最初Unix IPC包括:管道、FIFO、訊號。System V IPC包括:System V訊息佇列、System V訊號燈、System V共享記憶體區。由於Unix版本的多樣性,電子電氣工程協會(

帶輸入查詢功能匹配拉框的實現方式

sae idt hwnd 就會 bfd bmgr 使用方法 oaf adt 在Web開發中我們經常需要用戶進行輸入操作,輸入框內我們輸入幾個字,輸入框就會出現下拉提示你可能要輸入的完整信息。下面我總結了幾種常見的方案: 一:EasyUi combobox 組合框 具體使用方

C#執行緒間同步實現方式

一、使用訊號量 using System; using System.Threading; namespace SemaphoreDemo { class Program { static void Main(string[] args)

多執行緒-執行緒同步實現方式

執行緒同步有幾種實現方式 1. Synchronized 在方法級別  public synchronized …. 在程式碼塊   synchronized(物件){} 1. 當synchronized作用在方法上的時候,鎖住的就是這個物件的例項 synchronized

多線程有實現方法?同步實現方法?(被問到)

所有 正在 () read 異常 同步 -h 競爭 sync 多線程有兩種實現方法,分別是繼承Thread類與實現Runnable接口 同步的實現方面有兩種,分別是synchronized,wait與notify wait():使一個線程處於等待狀態,並且釋放所持有的對象的

單例模式的實現方式

str 類加載 代碼 創建對象 stat 懶漢 導致 方法 不支持 1、懶漢式,線程不安全 是否 Lazy 初始化:是 是否多線程安全:否 實現難度:易 描述:這種方式是最基本的實現方式,這種實現最大的問題就是不支持多線程。因為沒有加鎖 synchronized,所以嚴格意

分布式session的實現方式

服務器 單點 redis 容易 穩定性 常用 str 廣播 cati 1.基於數據庫的session共享 2.基於NFS共享文件系統 3.基於memcached 的session,怎麽保證session的高可用 4.基於resin/tomcat web容器本身的sessio

單例模式實現方式

size ron jdk null singleton bsp 還要 一個 fin 1、餓漢式:靜態常量 特點:單例的實例被聲明成static和final變量了,在第一次加載類到內存中時就會初始化,所以會創建實例本身是線程安全的 public class Sin

Java單例模式實現方式

開始 名稱 常量 就是 多線程開發 靜態代碼塊 浪費 ack 多線程同步 在平時的工作、學員的學習以及面試過程中,單例模式作為一種常用的設計模式,會經常被面試官問到,甚至筆試會要求學員現場默寫,下面將會就單例模式的實現思路和幾種常見的實現方式進行簡單的分享。

分布式鎖的實現方式

article nod process 過程 快速 details 一段時間 detail 領域 一、為什麽要使用分布式鎖 為了保證一個方法或屬性在高並發情況下的同一時間只能被同一個線程執行,在傳統單體應用單機部署的情況下,可以使用Java並發處理相關的API(如Reent

Java 定時任務的實現方式

java作業調度 tails 監聽器 ever 觸發 posit exist ttr 輕量級 JAVA實現定時任務的幾種方式 @(JAVA)[spring|quartz|定時器]   近期項目開發中需要動態的添加定時任務,比如在某個活動結束時,自動生成獲獎名單,導出exce

【小家java】Session和Cookie的區別和聯絡、分散式session的實現方式

相關閱讀 【小家java】java5新特性(簡述十大新特性) 重要一躍 【小家java】java6新特性(簡述十大新特性) 雞肋升級 【小家java】java7新特性(簡述八大新特性) 不溫不火 【小家java】java8新特性(簡述十大新特性) 飽受讚譽 【小家java】java9

最近在研究多線程,淺談JAVA中多線程的實現方式

進行 數據 使用 導致 效率問題 多線程 方法 sta img 多線程的實現方式:   個人認為,要說多線程的實現方式,萬變不離其宗,最基本的就是兩種1.繼承Thread類;2.實現runnable接口,本質上來說就是用來啟動線程執行任務的過程,具體來說的話,通過這

垂直水平居中的實現方式

一.使用彈性盒模型 display: flex      1. 在你不知道當前元素的寬高的情況下 你可以為父元素設定 display: flex;樣式 並設定 align-items: center; justify-content: center  

字串反轉的實現方式?陣列反轉

1. strrev(); 只對英文友好 2. function getRev($str,$encoding='utf-8'){ $result = ''; $len = mb_strlen($str); for($i=$len-1; $i>=0; $

Python的實現方式

Python自身作為一門程式語言,它有多種實現。這裡的實現指的是符合Python語言規範的Python解釋程式以及標準庫等。這些實現雖然實現的是同一種語言,但是彼此之間,特別是與CPython之間還是有些差別的。 下面分別列出幾個主要的實現。 1.CPython:這是Python

VUE 全域性變數的實現方式

1、全域性變數專用模組 意思是說,用一個模組(js or vue)管理這套全域性變數,模組裡的變數用export (最好匯出的格式為物件,方便在其他地方呼叫)暴露出去,當其它地方需要使用時,用import 匯入該模組 全域性變數專用模組Global.vue const colorLi

佇列的實現方式

佇列簡介: 佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,和棧一樣,佇列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。 佇列是一種最常用的資料結構,也是最重要的一種資