1. 程式人生 > >ios高級開發之多線程(三)GCD技術

ios高級開發之多線程(三)GCD技術

屬性 brush 以及 efault eas 5.1 線程休眠 thread nil

GCD是基於C的API,它是libdispatch的的市場名稱。而libdispatch作為Apple公司的一個庫,為並發代碼在多核硬件(跑IOS或者OS X)上執行提供有力支持。

那麽我們為什麽要用GCD技術呢?

1.GCD能夠推遲昂貴的計算任務,並在後臺運行它們來改善你的應用的性能。

2.GCD提供一個易於使用的並發模型而不僅僅是鎖和線程。以幫助我們避開並發陷阱。

3.GCD具有在常見模式(比如單例)上用更高性能的原語優化你的代碼的潛在能力。

4.GCD旨在替換NSTread等線程技術。

5.GCD可充分利用設備的多核。

6.GCD可自動管理線程的生命周期。

說了這些GCD的優點,那麽在實際開發中,如何使用GCD來更好滿足我們的需求呢?

一、Synchronous&Asynchronous 同步&異步

1.Synchronous同步:同步任務的執行的方式:在當前線程中執行,必須等待當前語句執行完畢,才會執行下一條語句。

來看下同步的代碼:

//同步的打印順序
-(void)syncTask{
    NSLog(@"begin");
    //GCD的同步方法
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //任務中要執行的代碼
        [NSThread sleepForTimeInterval:2.0
]; NSLog(@"%@",[NSThread currentThread]); }); NSLog(@"end"); }

來看打印出來的:

2019-03-29 12:00:54.993042+0800 wftest[5191:88411] begin

2019-03-29 12:00:56.994525+0800 wftest[5191:88411] <NSThread: 0x600000ff5380>{number = 1, name = main}

2019-03-29 12:00:56.994799+0800 wftest[5191:88411] end

可以看到,即使線程休眠了2秒,他依然會按照順序執行,等代碼塊內的代碼執行完畢後,才會執行end.

接著我們再來看異步,不在當前線程中執行,不用等當前語句執行完畢,就可以執行下一條語句

來看代碼:

//異步順序
-(void)asyncTask{
    //異步不會在當前線程執行,首先需要開辟新的線程,而開辟新的線程也需要一定的時間
    NSLog(@"begin");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    NSLog(@"end");
}

  我們來看下打印的情況:

2019-03-29 16:06:34.600530+0800 wftest[1336:19777] begin

2019-03-29 16:06:34.600763+0800 wftest[1336:19777] end

2019-03-29 16:06:34.600892+0800 wftest[1336:20090] <NSThread: 0x600000acc600>{number = 3, name = (null)}

可以看到,打印出來begin後,直接打印出了end.然後才執行了異步塊裏的代碼。

接下來,我們來看看串行隊列Serial queues,和並行隊列(並發隊列)Concurrent queues.

1.串行隊列的特點:

以先進先出的方式執行,順序調度隊列中的任務執行。

無論隊列中的任務函數是同步還是異步,都會等待前一個任務執行完成後,再調度後面的任務。

我們先來看看,串行隊列的同步代碼:

//串行隊列同步函數(在一個線程中執行,註意,GCD的是C語言API,不要和OC弄混)
-(void)serialSync{
    //這裏有兩個參數,第一個參數的標識符,一般為公司域名倒寫,第二個參數隊列類型,DISPATCH_QUEUE_SERIAL串行,DISPATCH_QUEUE_CONCURRENT為並發隊列
    dispatch_queue_t  serialQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_SERIAL);
    //創建任務
    void (^task1) (void) = ^(){
        NSLog(@"task1---%@",[NSThread currentThread]);
    };
    
    void (^task2) (void) = ^(){
        NSLog(@"task2 --  %@",[NSThread currentThread]);
    };
    
    void (^task3) (void) = ^(){
        NSLog(@"task3  -- %@",[NSThread currentThread]);
    };
    
    //添加任務到隊列,同步執行方法
    dispatch_sync(serialQueue, task1);
    dispatch_sync(serialQueue, task2);
    dispatch_sync(serialQueue, task3);
}

  然後來看下NSLog打印的東西:

2019-03-29 17:08:10.362074+0800 wftest[2989:50297] task1---<NSThread: 0x600002009340>{number = 1, name = main}

2019-03-29 17:08:10.364550+0800 wftest[2989:50297] task2 -- <NSThread: 0x600002009340>{number = 1, name = main}

2019-03-29 17:08:10.365860+0800 wftest[2989:50297] task3 -- <NSThread: 0x600002009340>{number = 1, name = main}

可以看到task1,taks2,task3是完全按照順序執行的。

再來看串行隊列的異步方法:

先來看代碼

//串行隊列異步函數
-(void)serialAsync{
    //創建一個串行隊列
    dispatch_queue_t serialQuene = dispatch_queue_create("com.feng", DISPATCH_QUEUE_SERIAL);
    //2.創建任務
    void (^task1)(void) = ^(){
        NSLog(@"task1 --- %@",[NSThread currentThread]);
    };
    void (^task2)(void) = ^(){
        NSLog(@"task 2-- %@",[NSThread currentThread]);
    };
    void (^task3)(void) = ^(){
        NSLog(@"task 3 -- %@",[NSThread currentThread]);
    };
    
    //3.添加任務隊列
    dispatch_async(serialQuene, task1);
    dispatch_async(serialQuene, task2);
    dispatch_async(serialQuene, task3);
}

 來看下打印結果:

2019-03-30 14:27:21.730761+0800 wftest[4929:84017] 主線程 -- <NSThread: 0x600000650540>{number = 1, name = main}
2019-03-30 14:27:21.731252+0800 wftest[4929:84090] task1 --- <NSThread: 0x600000638340>{number = 3, name = (null)}
2019-03-30 14:27:21.731536+0800 wftest[4929:84090] task 2-- <NSThread: 0x600000638340>{number = 3, name = (null)}
2019-03-30 14:27:21.731691+0800 wftest[4929:84090] task 3 -- <NSThread: 0x600000638340>{number = 3, name = (null)}

  

可以看到,串行隊列異步執行,仍然按順序執行的。也就是說,只要是串行隊列,無論是異步,還是同步函數,都是按順序執行的。

2.看完串行隊列,我們來看並發(並行)隊列。

並發隊列的特點:

1.以先進先出的方法,並發調度隊列中的任務的執行。

2.如果是並發隊列的同步執行,就會等先被調度的任務執行完畢後,再執行下一個任務。

3.如果是並發隊列的異步執行,同時底層線程池有可用的線程資源,會在新的任務調度後,調度下一個任務。

也就是說,先加進來的任務會先被執行,但不用等他執行完畢,就可以接著調度下一個任務。

那麽,我們先來看並發隊列的同步任務的代碼:

//並發隊列同步函數
-(void)concurrentSync{
    //1.創建並發隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_CONCURRENT);
    
    //2.創建任務
    void (^task1) (void) = ^(){
        NSLog(@"task1 -- %@",[NSThread currentThread]);
    };
    void (^task2) (void) = ^(){
        NSLog(@"task2 -- %@",[NSThread currentThread]);
    };
    void (^task3) (void) = ^(){
        NSLog(@"task3 -- %@",[NSThread currentThread]);
    };
    
    //3.添加同步任務到並發隊列
    dispatch_sync(concurrentQueue, task1);
    dispatch_sync(concurrentQueue, task2);
    dispatch_sync(concurrentQueue, task3);
}

  再來看打印情況:

2019-03-30 14:01:51.358796+0800 wftest[4073:69627] 主線程 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
2019-03-30 14:01:51.359220+0800 wftest[4073:69627] task1 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
2019-03-30 14:01:51.359668+0800 wftest[4073:69627] task2 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
2019-03-30 14:01:51.360195+0800 wftest[4073:69627] task3 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}

  

  

  

可以看到,雖然是並發隊列,但因為是同步任務,所以也是按順序執行的。因為是同步任務,所以就在當前線程,主線程中執行。異步任務則會在子線程中執行。

(可以簡單總結:串行 ,要等待上個任務執行完畢,才執行下個任務,所以會在同一個線程中執行。 並行:不用等上個任務執行完畢,就可以執行下個任務。同步:在當前線程中執行,不會開辟子線程。異步:在子線程中執行(這是指串行和並行隊列。後面說的主隊列異步,也是在主線程中執行))。

再來看並發隊列的異步執行任務:

//並發隊列的異步執行
-(void)concurrentAsyn{
    //1.創建隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_CONCURRENT);
    //2.創建任務
    void(^task1) (void) = ^(){
        NSLog(@"task1 --   %@",[NSThread currentThread]);
    };
    void(^task2) (void) = ^(){
        NSLog(@"task2 -- %@",[NSThread currentThread]);
    };
    void(^task3) (void) = ^(){
        NSLog(@"task3 -- %@",[NSThread currentThread]);
    };
    //3.把任務添加到隊列中去
    dispatch_async(concurrentQueue, task1);
    dispatch_async(concurrentQueue, task2);
    dispatch_async(concurrentQueue, task3);
}

  來看打印情況:

2019-03-30 14:02:45.003384+0800 wftest[4112:70392] 主線程 -- <NSThread: 0x600000ba8c40>{number = 1, name = main}
2019-03-30 14:02:45.005834+0800 wftest[4112:70450] task2 -- <NSThread: 0x600000bf7840>{number = 4, name = (null)}
2019-03-30 14:02:45.005834+0800 wftest[4112:70449] task1 --   <NSThread: 0x600000bcc380>{number = 3, name = (null)}
2019-03-30 14:02:45.005838+0800 wftest[4112:70454] task3 -- <NSThread: 0x600000bcc480>{number = 5, name = (null)}

  可以看到,異步任務另外開辟了子線程。可以看到打印順序發生了變化。

接著,我們來看全局隊列。

全局隊列的工作表現和並發隊列一致。

但是全局隊列是否就是並發隊列呢?不是的。我們來看下他們的區別:

1.全局隊列沒有名稱,無論是MRC&ARC都不用考慮釋放,所以在日常開發中,建議使用全局隊列。

2.並發隊列:有名字,和NSThread的name屬性作用類似,如果你在MRC的開發中,則需要使用dispatch_releas(q)來釋放對應的對象。

那麽並發隊列在什麽時候使用呢?在你開發第三方的框架的時候,則需要使用並發隊列了。這樣可以避開和使用你的開發框架的程序員弄混隊列。

咱們先來看下全局隊列的同步任務。(日常開發中幾乎用不到。)

//全局隊列的同步任務
-(void)globalSync{
    NSLog(@"begin");
    //1.創建全局隊列
    dispatch_queue_t gloabalQueue = dispatch_get_global_queue(0, 0);
    //2.創建任務
    void(^task1) (void) = ^(){
        NSLog(@"task1 --   %@",[NSThread currentThread]);
    };
    void(^task2) (void) = ^(){
        NSLog(@"task2 -- %@",[NSThread currentThread]);
    };
    void(^task3) (void) = ^(){
        NSLog(@"task3 -- %@",[NSThread currentThread]);
    };
    //3.加入任務到隊列中執行
    dispatch_sync(gloabalQueue, task1);
    dispatch_sync(gloabalQueue, task2);
    dispatch_sync(gloabalQueue, task3);
    NSLog(@"end");
}

  來看打印結果:

2019-03-30 14:35:08.160967+0800 wftest[5189:88230] 主線程 -- <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161223+0800 wftest[5189:88230] begin
2019-03-30 14:35:08.161470+0800 wftest[5189:88230] task1 --   <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161639+0800 wftest[5189:88230] task2 -- <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161773+0800 wftest[5189:88230] task3 -- <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161891+0800 wftest[5189:88230] end

  可以看到,按熟悉執行,同步的,都是當前線程中執行的。

再來看全局隊列的異步任務,他是在子線程池上執行的,每個任務都有一個自己的線程,前提是線程池裏有線程資源,底層有一個線程重用機制的。看下代碼:

2019-03-30 14:39:06.429812+0800 wftest[5330:90708] 主線程 -- <NSThread: 0x600003606580>{number = 1, name = main}
2019-03-30 14:39:06.430060+0800 wftest[5330:90708] begin
2019-03-30 14:39:06.430228+0800 wftest[5330:90708] end
2019-03-30 14:39:06.430381+0800 wftest[5330:90762] task1 --   <NSThread: 0x600003660a40>{number = 3, name = (null)}
2019-03-30 14:39:06.430416+0800 wftest[5330:90763] task3 -- <NSThread: 0x600003660a00>{number = 4, name = (null)}
2019-03-30 14:39:06.430422+0800 wftest[5330:90760] task2 -- <NSThread: 0x600003660ec0>{number = 5, name = (null)}

  可以看到,每個任務都有自己的獨立的線程。

有點累,一會再來看看主隊列。

主隊列的特點:

1.專門用來在主線程上調度任務的隊列。

2.不會開啟子線程

3.以先進先出的方式,在主線程空閑的時候才會調度主隊列中的任務在主線程中執行。

4.如果當前主線程中有任務在執行,那麽無論主隊列中添加了什麽任務,都不會被調度。

主隊列是負責在主線程中調度任務的。

會隨著程序啟動一起創建。

對於我們程序員來說,主隊列只需要獲取,不需要創建。

那麽我們來看下主隊列的異步任務的代碼:

//主隊列的異步任務
-(void)mainAsync{
    NSLog(@"begin");
    
    //1.創建主隊列
    dispatch_queue_main_t mainAsync = dispatch_get_main_queue();
    
    //2.創建任務
    void(^task1) (void) = ^(){
        NSLog(@"task1 --   %@",[NSThread currentThread]);
    };
    void(^task2) (void) = ^(){
        NSLog(@"task2 -- %@",[NSThread currentThread]);
    };
    void(^task3) (void) = ^(){
        NSLog(@"task3 -- %@",[NSThread currentThread]);
    };
    //添加任務到隊列
    dispatch_async(mainAsync, task1);
    dispatch_async(mainAsync, task2);
    dispatch_async(mainAsync, task3);
    NSLog(@"end");
}

  來看看打印情況:

2019-04-01 16:23:52.002922+0800 wftest[2320:37104] 主線程 -- <NSThread: 0x600001676a80>{number = 1, name = main}
2019-04-01 16:23:52.003178+0800 wftest[2320:37104] begin
2019-04-01 16:23:52.003342+0800 wftest[2320:37104] end
2019-04-01 16:23:52.092118+0800 wftest[2320:37104] task1 --   <NSThread: 0x600001676a80>{number = 1, name = main}
2019-04-01 16:23:52.092324+0800 wftest[2320:37104] task2 -- <NSThread: 0x600001676a80>{number = 1, name = main}
2019-04-01 16:23:52.092497+0800 wftest[2320:37104] task3 -- <NSThread: 0x600001676a80>{number = 1, name = main}

  大家可以註意到這裏的幾個情況:

1.雖然是異步的,但是三個任務仍然按順序調度執行。

2.先執行了begin,緊接著執行的了end.然後才執行了三個任務,也就是說,主線程有空閑的時候才執行這三個任務。

我們說下deadlock死鎖:是兩個或者更多的線程之間出現的情況:比如第一個線程在等待第二個線程的完成才能繼續執行,而第二個線程在等待第一個線程的完成才能繼續執行。

看起來似乎異步更有用,效率更高,那麽同步有什麽用呢,我們說下同步的作用。

1.首先,同步肯定是保證了任務執行的順序。

2.可以讓後面的異步任務要依賴於某一個同步的任務。比如,必須讓用戶登錄之後,才允許他下載電影。

我們看下代碼:

//同步+異步
-(void)loadMovies{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{//開辟一條子線程
        NSLog(@"開辟了子線程----%@",[NSThread currentThread]);
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            //登錄,在當前的線程執行
            NSLog(@"登錄了---%@", [NSThread currentThread]);
            sleep(3);
        });
        
        //2.同時下載3部電影
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"正在下載第一部電影---%@",[NSThread currentThread]);
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"正在下載第二部電影---%@",[NSThread currentThread]);
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"正在下載第三部電影---%@",[NSThread currentThread]);
        });
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"計算機將在三秒後關閉 --%@",[NSThread currentThread]);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"關機了---%@", [NSThread currentThread]);
           });
        });
    });
}

  然後我們來看打印log:

2019-04-01 17:52:46.491858+0800 wftest[5359:81060] 開辟了子線程----<NSThread: 0x600002ff8580>{number = 3, name = (null)}
2019-04-01 17:52:46.492263+0800 wftest[5359:81060] 登錄了---<NSThread: 0x600002ff8580>{number = 3, name = (null)}
2019-04-01 17:52:49.497610+0800 wftest[5359:81063] 正在下載第一部電影---<NSThread: 0x600002ffaa00>{number = 4, name = (null)}
2019-04-01 17:52:49.497634+0800 wftest[5359:81062] 正在下載第三部電影---<NSThread: 0x600002ffefc0>{number = 6, name = (null)}
2019-04-01 17:52:49.497654+0800 wftest[5359:81061] 正在下載第二部電影---<NSThread: 0x600002ffef40>{number = 5, name = (null)}
2019-04-01 17:52:50.498825+0800 wftest[5359:80998] 計算機將在三秒後關閉 --<NSThread: 0x600002f9d600>{number = 1, name = main}
2019-04-01 17:52:53.752223+0800 wftest[5359:80998] 關機了---<NSThread: 0x600002f9d600>{number = 1, name = main}

  我們註意到這幾個方面:雖然下載電影的時候,又開啟了三個新的線程,但是他們仍然要等待登錄後,才能執行,以及最後,計算機回到主隊列去關閉計算機的時候,也是等電影下載完畢。這是因為主隊列這裏的也是同步任務。前面也是同步任務。

接下來我們來看下dispatch_time的延遲操作

什麽時候使用dispatch_after呢?

1.最好堅持在主隊列上使用dispatch_after。而不是在自定義串行隊列上,並發隊列也盡量不要使用。

2.主隊列(串行)是使用dispatch_after的最好選擇。xcode也提供了自動完成模板。

我們來看下代碼:

//延遲執行
-(void)delay{
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
    void(^task)(void)=^(){
        NSLog(@"%@",[NSThread currentThread]);
    };
    //主隊列
    dispatch_after(when, dispatch_get_main_queue(), task);
     NSLog(@"come here");
}

  來看打印:

2019-04-02 10:55:47.020688+0800 wftest[2069:28686] come here
2019-04-02 10:55:49.216037+0800 wftest[2069:28686] <NSThread: 0x6000018e1c40>{number = 1, name = main}

IOS提供的一些方便使用的延遲:

//延遲執行
-(void)after{
    [self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0];
}

  再來看看線程安全:

線程安全是多線程不可避免的問題。

dispatch_once以線程安全的方式執行,僅且執行代碼一次。她會給代碼設立一個臨界區。試圖訪問臨界區(即要傳遞給dispatch_onece的代碼)的不同線程,在臨界區已經有一個線程在執行的情況下會被阻塞,直到臨界區完成為止。

我們來看下使用dispatch_once來實現單例線程安全:

//使用dispatch_once實現線程安全的單例
+(instancetype)sharedSingleton{
    static id instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

 如果一個單例中的單例屬性是一個可變對象。那麽就要考慮線程安全問題了。比如NSMutableArray。可能會出現一個線程正在讀取,另外一個線程正在修改。這樣就會出現線程不安全的情況。在GCD中可以通過dispatch_barrier_async來進行創建讀寫鎖,這樣一個解決方案。

接下來,我們來看下調度組(dispatch_group):

調度組的實現原理:類似引用計數,進行+1,-1;

應用場景:

比如當你開啟了下載任務,當下載三個任務,只有等這三個任務全部下載完畢後,才能下一步做事情。這個時候就可以用到調度組,這個調度組,就能監聽它裏面的任務是否執行完畢:

//調度組
-(void)groupDispatch{
    //1.創建調度組
    dispatch_group_t group = dispatch_group_create();
    //2.獲取全局隊列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //3.創建三個下載任務
    void(^task1)(void) = ^(){
        NSLog(@"%@----下載片頭",[NSThread currentThread]);
    };
    
    dispatch_group_enter(group);//引用計算+1
    void (^task2) (void) = ^(){
        NSLog(@"%@---下載內容",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3.0];
        NSLog(@"----下載內容完畢");
        dispatch_group_leave(group);//引用計數-1
    };
    
    dispatch_group_enter(group);//引用計數+1
    void(^task3)(void)=^(){
        NSLog(@"%@----下載片尾",[NSThread currentThread]);
        dispatch_group_leave(group);//引用計數-1
    };
    
    //4.需要將我們的隊列和任務放到組內去監控
    dispatch_group_async(group, queue, task1);
    dispatch_group_async(group, queue, task2);
    dispatch_group_async(group, queue, task3);
    
    //5.監聽函數
    // 參數2,表示參數3這裏的代碼在哪個隊列中執行
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //表示組內所有的任務都完成之後,執行這裏的代碼
        NSLog(@"把下載好的視頻按順序拼接好,然後顯示在UI上播放%@",[NSThread currentThread]);
    });
    
}

  看打印:

2019-04-02 16:18:45.168288+0800 wftest[12368:305146] <NSThread: 0x6000023d5fc0>{number = 4, name = (null)}---下載內容
2019-04-02 16:18:45.168288+0800 wftest[12368:305147] <NSThread: 0x6000023d5f00>{number = 3, name = (null)}----下載片頭
2019-04-02 16:18:45.168288+0800 wftest[12368:305149] <NSThread: 0x6000023e9c80>{number = 5, name = (null)}----下載片尾
2019-04-02 16:18:48.174292+0800 wftest[12368:305146] ----下載內容完畢
2019-04-02 16:18:48.174604+0800 wftest[12368:305085] 把下載好的視頻按順序拼接好,然後顯示在UI上播放<NSThread: 0x6000023b25c0>{number = 1, name = main}

  dispatch_group_enter手動通知group任務已經開始。註意,enter和leave必須成對。否則會造成詭異崩潰問題。

最後,再來看定時源事件和子線程的運行循環:

-(void)myMain{
    //1.定義一個定時器
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvnet) userInfo:nil repeats:YES];
    //2.將定時器加入到運行循環中,只有當加入到運行循環中,他才知道這個時候,有一個定時任務
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

-(void)timeEvnet{
    NSLog(@"%d----%@",self.count,[NSThread currentThread]);
    if(self.count++ == 10){
        NSLog(@"掛了");
        //停止當前的運行循環
        CFRunLoopStop(CFRunLoopGetCurrent());
    }
}

  

ios高級開發之多線程(三)GCD技術