GCD

封裝GCD以及介紹如何使用

 

 

源碼地址 http://pan.baidu.com/s/1zTUR8html

研究GCD有一段時間,翻譯了多篇文章,找了不少的資料,看了不少官方文檔,看起來很難,實際上很簡單,本人一一進行講解怎麼使用.多線程

支持ARC以及非ARC,不管在ARC環境仍是在非ARC環境,都須要調用dispatchRelease方法來釋放init出的GCDGroup,GCDQueue,GCDSemaphore,以及GCDTimer.併發

 

1. 系統併發線程隊列app

    [[GCDQueue globalQueue] execute:^{
        // 在系統默認級別的線程隊列中執行併發的操做
    }];

其實是在這個線程隊列中dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)執行操做,官方文檔以下less

The well-known global concurrent queues cannot be modified. Calls to dispatch_suspend, dispatch_resume, dispatch_set_context, and the like have no effect when used with queues returned by this function.
Blocks submitted to these global concurrent queues may be executed concurrently with respect to each other.異步

這個婦孺皆知的的併發隊列是不能更改的,因此,你調用方法dispatch_suspend,dispatch_resume以及dispatch_set_context等等,都是沒有效果的.被提交到這個線程隊列的block將會被併發的執行,不存在前後順序.async

請看如上結果,執行沒有前後順序,是同時執行的.post

 

2. 系統串行線程隊列測試

    [[GCDQueue mainQueue] execute:^{
        // 在系統主線程隊列中執行串行操做
    }];

The main queue is automatically created by the system and associated with your application’s main thread.this

主線程隊列是被系統自動建立的,用來關聯上你的應用的主線程.

As with the global concurrent queues, calls to dispatch_suspend, dispatch_resume, dispatch_set_context, and the like have no effect when used with queues returned by this function.

做爲全局的併發隊列,調用dispatch_suspend,dispatch_resume,dispatch_set_context相似的方法都將無效.

請看如上結果,執行有前後順序.

問:既然有前後順序,也有多是先執行了第一個block,再執行了第二個block...,只是時間很短,打印出有順序而已,也有多是一塊兒執行的,對吧?

先看看官方dispatch_async方法的文檔:

This function is the fundamental mechanism for submitting blocks to a dispatch queue. Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. The target queue determines whether the block is invoked serially or concurrently with respect to other blocks submitted to that same queue. Independent serial queues are processed concurrently with respect to each other.

這是個基礎的提交block到一個指定queue的方法.只要提交了這個block,這個方法就會當即返回,根本就不會等待看這個block是否被調用了.這個指定的queue的類型決定了這個block是不是串行的執行抑或是併發的執行.獨立的串行隊列將會對每個block按照順序執行併發操做.

我測試了下系統主線程隊列的執行狀況,代碼以下,結論是:必須是上一個串行的block執行完畢後纔會執行下一個block.

複製代碼
    [[GCDQueue mainQueue] execute:^{
        NSLog(@"1");
        for (int i = 0; i < 1000000000; i++) {
            static int flag = 1;
            flag++;
        }
        NSLog(@"kkkkkk");
    }];
    
    [[GCDQueue mainQueue] execute:^{
        NSLog(@"2");
    }];
    
    [[GCDQueue mainQueue] execute:^{
        NSLog(@"3");
    }];
    
    [[GCDQueue mainQueue] execute:^{
        NSLog(@"4");
    }];
    
    [[GCDQueue mainQueue] execute:^{
        NSLog(@"5");
    }];
複製代碼

再測試了下本身建立的串行隊列

複製代碼
    GCDQueue *queue = [[GCDQueue alloc] initSerial];

    [queue execute:^{
        NSLog(@"1");
        for (int i = 0; i < 1000000000; i++) {
            static int flag = 1;
            flag++;
        }
        NSLog(@"kkkkkk");
    }];
    
    [queue execute:^{
        NSLog(@"2");
    }];
    
    [queue execute:^{
        NSLog(@"3");
    }];
    
    [queue execute:^{
        NSLog(@"4");
    }];
    
    [queue execute:^{
        NSLog(@"5");
    }];
複製代碼

至少從打印結果處得知,是按照序列來執行block的,只有上一個block執行完畢了纔會執行下一個block,認識這一點很重要哦.

複製代碼
    [[GCDQueue globalQueue] execute:^{
        // 併發線程執行阻塞操做
        
        [[GCDQueue mainQueue] execute:^{
            // 主線程更新UI
        }];
    }];
複製代碼

是最經常使用的一種方式,也是你們見得最多的方式.

 

3. 在某個指定的隊列中執行延時操做

    [[GCDQueue globalQueue] execute:^{
        
        // 延時3秒後執行操做
        
    } afterDelay:3 * NSEC_PER_SEC];

-dispatch_after-

Enqueue a block for execution at the specified time.
This function waits until the specified time and then asynchronously adds block to the specified queue.

將一個要執行的並設定了時間block插入隊列.

這個方法會等到指定的時間,異步的將block添加到指定的隊列中.

從如下代碼中的執行結果能夠看出,即便提交了任務,若是出現了死循環,後面提交的任務也是沒法執行的.

複製代碼
   [[GCDQueue mainQueue] execute:^{
        
        NSLog(@"1");
        while (1)
        {
            
        }
        
    } afterDelay:3 * NSEC_PER_SEC];
 
    
    [[GCDQueue mainQueue] execute:^{
        
        NSLog(@"2");
        
    } afterDelay:5 * NSEC_PER_SEC];
複製代碼

下面的這個例子,就能看出,只要一個提交的任務可以在時間點上完成,是不會影響後面的任務執行的,可是若是把sleep(1)改爲sleep(3),就會影響後面提交的任務執行.

複製代碼
    [[GCDQueue mainQueue] execute:^{
        
        NSLog(@"1");
        sleep(1);
        
    } afterDelay:3 * NSEC_PER_SEC];
 
    
    [[GCDQueue mainQueue] execute:^{
        
        NSLog(@"2");
        
    } afterDelay:5 * NSEC_PER_SEC];
複製代碼

 

4. 在group中監聽某些線程完成了,以後再執行某個線程

複製代碼
    GCDGroup *group = [GCDGroup new];

    [[GCDQueue globalQueue] execute:^{
        // 代碼
    } inGroup:group];
    
    [[GCDQueue globalQueue] execute:^{
        // 代碼
    } inGroup:group];
    
    [[GCDQueue globalQueue] execute:^{
        // 代碼
    } inGroup:group];
    
    [[GCDQueue globalQueue] notify:^{
        // 監聽group中的其餘的任務完成後纔會執行到此處
    } inGroup:group];
複製代碼

-dispatch_group_notify-

This function schedules a notification block to be submitted to the specified queue when all blocks associated with the dispatch group have completed. If the group is empty (no block objects are associated with the dispatch group), the notification block object is submitted immediately.

這個方法安排了一個通知用的block到這個指定的queue當中,而當全部與這個group相關聯的block都執行完畢了,纔會執行這個通知的block.若是這個組空了,那這個通知用的block就會被當即的執行.

另外一種監聽線程執行完畢的方式,不過可以設定超時時間

複製代碼
    GCDGroup *group = [GCDGroup new];

    [group enter];
    [[GCDQueue globalQueue] execute:^{
        
        [group leave];
    }];
    
    [group enter];
    [[GCDQueue globalQueue] execute:^{
        
        [group leave];
    }];

    [[GCDQueue globalQueue] execute:^{
        [group wait:3 * NSEC_PER_SEC];
        
        // 若是超時了3秒,上面的線程還沒執行完,就跳過了
    }];
複製代碼

請注意,enter與leave必須成對出現!

-dispatch_group_enter-

Explicitly indicates that a block has entered the group.

精確指定一個block進入了group.
Calling this function increments the current count of outstanding tasks in the group. Using this function (with dispatch_group_leave) allows your application to properly manage the task reference count if it explicitly adds and removes tasks from the group by a means other than using the dispatch_group_async function. A call to this function must be balanced with a call to dispatch_group_leave. You can use this function to associate a block with more than one group at the same time.

調用這個方法,將會增長當前在組中未解決的任務的數量.使用這個方法是爲了可以管理任務的細節,指定何時結束這個任務,你也可使用dispatch_group_async這個方法.調用了這個方法就必須使用dispatch_group_leave來平衡.你能夠同時的給一個block關聯上不一樣的group.

dispatch_group_enter 與 dispatch_group_leave 可以處理更加複雜的任務類型,推薦!

 

5. 使用定時器

複製代碼
    GCDTimer *timer = [[GCDTimer alloc] initInQueue:[GCDQueue globalQueue]];
    [timer event:^{
        
        // 每1秒執行一次你的event
        
    } timeInterval:1 * NSEC_PER_SEC];
    [timer start];
複製代碼

此定時器不能暫停,只能銷燬後釋放掉對象.

    [timer destroy];
    [timer dispatchRelease];

 

6. 使用信號量

複製代碼
    GCDSemaphore *sem = [[GCDSemaphore alloc] init];
    
    GCDTimer *timer = [[GCDTimer alloc] initInQueue:[GCDQueue globalQueue]];
    [timer event:^{
        [sem signal];
    } timeInterval:NSEC_PER_SEC];
    [timer start];
    
    [[GCDQueue globalQueue] execute:^{
        while (1)
        {
            [sem wait];
            NSLog(@"Y.X.");
        }
    }];
複製代碼

一個發送信號,一個接受信號

-dispatch_semaphore_signal-

Signals (increments) a semaphore.
Increment the counting semaphore. If the previous value was less than zero, this function wakes a thread currently waiting in dispatch_semaphore_wait.

This function returns non-zero if a thread is woken. Otherwise, zero is returned.

發送信號增長一個信號量.

增長一個信號量,若是當前值小於或者等於0,這個方法會喚醒某個使用了dispatch_semaphore_wait的線程.

若是這個線程已經喚醒了,將會返回非0值,不然返回0

 

 

 

基本已經講解完了GCD的部分功能,我會在附錄中講解GCDQueue類中下面列舉的方法如何使用以及有什麼含義,這些方法涉及到了多線程同步問題哦,比基礎用法有着更多的用處,更高級.

waitExecute:

barrierExecute:

waitBarrierExecute:

suspend

resume

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息