<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      一.GCD的簡介

       Grand Central Dispatch(GCD)是 Apple 開發的一個多核編程的較新的解決方法。它主要用于優化應用程序以支持多核處理器以及其他對稱多處理系統。它是一個在線程池模式的基礎上執行的并發任務。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。

         為什么使用GCD?

      1.GCD可用于多核的并行運算
      2.GCD會自動利用更多的CPU內核(比如雙核、四核)
      3.GCD會自動管理線程的生命周期(創建線程、調度任務、銷毀線程)
      4.程序員只需要告訴GCD想要執行什么任務,不需要編寫任何線程管理代碼
       
      二、GCD任務和隊列
       
         任務:就是執行操作的意思,換句話說就是你在線程中執行的那段代碼。在GCD中是放在block中的。執行任務有兩種方式:同步執行(sync)和異步執行(async)。兩者的主要區別是:是否在等待隊列的任務執行結束,以及是否具備開啟新線程的能力。
         
        同步執行(sync)
           同步添加任務到指定的隊列中,在添加的任務執行結束之前,會一直等待,直到隊列里面的任務完成之后在繼續執行。
           只能在當前線程中執行任務,不具備開啟新線程的能力
       異步執行(async)
           異步添加任務到指定的隊列中,它不會做任何等待,可以繼續執行任務。
           可以在新的線程中執行任務,具備開啟新線程的能力
       
      注意:異步執行(async)雖然具有開啟新線程的能力,但是并不一定開啟新線程。這跟任務所指定的隊列類型有關
       
      隊列(Dispatch Queue):這里的隊列指執行任務的等待隊列,即用來存放任務的隊列。隊列是一種特殊的線性表,采用FIFO(先進先出)的原則,即新任務總是被插入到隊列的末尾,而讀取任務的時候總是從隊列的頭部開始讀取。每讀取一個任務,則從隊列中釋放一個任務。隊列結構圖:

          在GCD中有兩種隊列:串行隊列和并發隊列。兩者都符合FIFO(先進先出)的原則,兩者的主要區別是:執行順序不同,以及開啟線程數不同。

        串行隊列(Serial Dispaych Queue):

            每次只有一個任務被執行。讓任務一個接著一個地執行。(只開啟一個線程,一個任務執行完畢后,在執行下一個任務)

        并發隊列(ConCurrent Dispatch Queue)

            可以讓多個任務并發(同時)執行。(可以開啟多個線程,并且同時執行任務)

      注意:并發隊列的并發功能只有在異步(dispatch_async)函數下才有效

      三、GCD 的使用步驟

        1.創建一個隊列(串行隊列或并發隊列)

        2.將任務追加到任務的等待隊列中,然后系統就會根據任務類型執行任務(同步執行或異步執行)

       隊列的創建方法/獲取方法

          可以使用dispatch_queue_create來創建隊列,需要傳入兩個參數,第一個參數表示隊列的唯一標識符,用于debug,可為空,Dispatch Queue的名稱推薦使用應用程序ID這種逆序全程域名;第二個參數用來識別是串行隊列還是并發隊列。DISPATCH_QUEUE_SERIAL 表示串行隊列,DISPATCH_QUEUE_CONCURRENT表示并發隊列。

      //    串行隊列的創建方法

          dispatch_queue_t queue = dispatch_queue_create("net.nange.test", DISPATCH_QUEUE_SERIAL);

      //    并發隊列的創建方法

          dispatch_queue_t queue1 = dispatch_queue_create("net.nange.test", DISPATCH_QUEUE_CONCURRENT)

         對于串行隊列,GCD提供了一種特殊的串行隊列:主隊列(Main Dispatch QUeue)

         所有放在主隊列中的任務,都會放到主線程中執行 

          可使用dispatch_get_main_queue()獲得主隊列

          // 主隊列的獲取方法

          dispatch_queue_t queue = dispatch_get_main_queue();

       

           對于并發隊列,GCD默認提供了全局并發隊列(Global Dispatch Queue)

          可以使用dispatch_get_global_queue來獲取。需要傳入兩個參數。第一個參數表示隊列的優先級,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個參數暫時沒用,用0即可。

        

      //全局并發隊列的獲取方法

          dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

       

        任務的創建方法

          GCD提供了同步執行任務的創建方法dispatch_sync和異步執行任務創建方法dispatch_async.

          //    同步執行任務創建方法

          dispatch_sync(queue, ^{

              //        這里放同步執行任務代碼

          });

      //    異步執行任務創建方法

          dispatch_async(queue, ^{

      //       這里放異步執行任務代碼

          });

        雖然使用GCD只需兩步,但是既然我們有兩種隊列(串行隊列/并發隊列),兩種任務執行方式(同步執行/異步執行),那么我們就有了四種不同的組合方式。這四種不同的組合方式是:

        1.同步執行 + 并發隊列

        2.異步執行 + 并發隊列

        3.同步執行 + 串行隊列

        4.異步執行 + 串行隊列

         實際上,剛才還說了兩種特殊的隊列:全局并發隊列、主隊列。全局并發隊列可以作為普通并發隊列來使用。但是主隊列因為有點特殊,所以我們就又多了兩種組合方式。這樣就有六種不同的組合方式了。

        5.同步執行 + 主隊列

        6.異步執行 + 主隊列

      那么這幾種不同的組合方式各有什么區別呢,直接看結果  再做講解

      四.GCD的基本使用

       先來講講并發隊列的兩種執行方式

      4.1 同步執行 + 并發隊列

          在當前線程中執行任務,不會開啟新線程,執行完一個任務,在執行下一個任務

      /*

       同步執行 + 并發隊列

       特點:在當前線程中執行任務,不會開啟新線程,執行完一個任務,在執行下一個任務

       */

      -(void)syncConcurrent{

          NSLog(@"currentThread---%@",[NSThread currentThread]);

          NSLog(@"syncConcurrent--begin");

          dispatch_queue_t queue = dispatch_queue_create("net.nange.QQ", DISPATCH_QUEUE_CONCURRENT);

          

          dispatch_sync(queue, ^{

           

           //       追加任務1

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

          });

          dispatch_sync(queue, ^{

      //      追加任務2

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          

          dispatch_sync(queue, ^{

      //   追加任務3

              for (int i=0 ; i<2; i++) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          NSLog(@"syncCurrent-----end");

      }

        4.2 異步執行 + 并發隊列

       可以開啟多個線程,任務交替(同時執行)

      /*

       異步執行 + 并發隊列

       特點:可以開啟多個線程,任務交替(同時執行)

       */

      -(void)asyncConcurrent{

          NSLog(@"currentThread---%@",[NSThread currentThread]);

          NSLog(@"asyncConcurrent--begin");

          dispatch_queue_t queue = dispatch_queue_create("net.nange.QQ", DISPATCH_QUEUE_CONCURRENT);

          

          dispatch_async(queue, ^{

           

           //       追加任務1

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

          });

          dispatch_async(queue, ^{

      //      追加任務2

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          

          dispatch_async(queue, ^{

      //   追加任務3

              for (int i=0 ; i<2; i++) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          NSLog(@"syncCurrent-----end");

      }

      4.3 同步執行 + 串行隊列

       不會開啟新線程,在當前線程執行任務。任務是串行的,執行完一個任務,在執行下一個任務

      /*

      同步執行 + 串行隊列

       特點:不回開啟新線程,在當前線程執行任務中。任務是串行的,在執行完一個任務,在執行下一個任務

       */

      -(void)syncSerial{

          NSLog(@"currentThread---%@",[NSThread currentThread]);

          NSLog(@"syncSerial--begin");

          dispatch_queue_t queue = dispatch_queue_create("net.nange.QQ", DISPATCH_QUEUE_SERIAL);

          

          dispatch_sync(queue, ^{

           

           //       追加任務1

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

          });

          dispatch_sync(queue, ^{

      //      追加任務2

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          

          dispatch_sync(queue, ^{

      //   追加任務3

              for (int i=0 ; i<2; i++) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          NSLog(@"syncCurrent-----end");

      }

      4.4 異步執行 + 串行隊列

       會開啟新線程,但是因為任務是串行的,執行完一個任務,在執行下一個任務

      /*

      異步執行 + 串行隊列

       特點:會開啟新線程,但是因為任務是串行的,執行完一個任務,在執行下一個任務

       */

      -(void)asyncSerial{

          NSLog(@"currentThread---%@",[NSThread currentThread]);

          NSLog(@"asyncSerial--begin");

          dispatch_queue_t queue = dispatch_queue_create("net.nange.QQ", DISPATCH_QUEUE_SERIAL);

          

          dispatch_async(queue, ^{

           

           //       追加任務1

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

          });

          dispatch_async(queue, ^{

      //      追加任務2

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          

          dispatch_async(queue, ^{

      //   追加任務3

              for (int i=0 ; i<2; i++) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          NSLog(@"syncCurrent-----end");

      }

         主隊列:GCD自帶的一種特殊的串行隊列

              所有放在主隊列中的任務,都會放到主線程中執行

              可使用disPatch_get_main_queue()獲得主隊列

      4.5 同步執行 + 主隊列

           同步執行 + 主隊列 在不用線程中調用結果也是不一樣,在主線程中調用會出現死鎖,而在其他線程中 則不會。

      4.5.1 在主線程中調用  (同步執行 + 主隊列)

           互相等待卡住不可行

           

      /*

      同步執行 + 主隊列

       特點(主線程調用):互等卡住不執行

       特點(其他線程調用):不會開啟新線程,執行完一個任務,再執行下一個任務。

       */

      -(void)syncMain{

          NSLog(@"currentThread---%@",[NSThread currentThread]);

          NSLog(@"asyncSerial--begin");

          dispatch_queue_t queue = dispatch_get_main_queue();

          

          dispatch_sync(queue, ^{

           

           //       追加任務1

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

          });

          dispatch_sync(queue, ^{

      //      追加任務2

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          

          dispatch_sync(queue, ^{

      //   追加任務3

              for (int i=0 ; i<2; i++) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          NSLog(@"syncCurrent-----end");

      }

          4.5.2  在其他線程中調用

           不會開啟新線程,執行完一個任務,在執行下一個任務

         使用NSThread 的detachNewthreadSelect  方法會創建線程,并自動啟動線程執行

         selector 任務

          [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];

      4.6 異步執行 +  主隊列

         只有在主線程中執行任務,執行完一個任務,在執行下一個任務

      /*

      異步執行 + 主隊列

       特點:只在主線程中執行任務,執行完一個任務,在執行下一個任務

       */

      -(void)asyncMain{

          NSLog(@"currentThread---%@",[NSThread currentThread]);

          NSLog(@"asyncSerial--begin");

          dispatch_queue_t queue = dispatch_get_main_queue();

          

          dispatch_async(queue, ^{

           

           //       追加任務1

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

          });

          dispatch_async(queue, ^{

      //      追加任務2

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          

          dispatch_async(queue, ^{

      //   追加任務3

              for (int i=0 ; i<2; i++) {

                  

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);//打印當前線程

              }

              

          });

          NSLog(@"syncCurrent-----end");

       

      }

        5.GCD線程間的通信

       iOS中 我們一般在主線程里邊進行UI刷新,例如:點擊、滾動、拖拽等事件。我們通常把一些耗時的操作放在其他線程,比如說圖片下載、文件上傳等耗時操作。而我們有時候在其他線程完成了耗時操作,需要回到主線程,那么就用到了線程之間的通訊。

        

      -(void)asyncMain{

      //  獲取全局并發隊列

          

          dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

      //   獲取主隊列

          dispatch_queue_t mainQueue = dispatch_get_main_queue();

          dispatch_async(queue, ^{

           

           //      異步 追加任務

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

              

      //        回到主線程

              dispatch_async(mainQueue, ^{

                  

      //            追加在主線程中執行的任務

                  [NSThread sleepForTimeInterval:2];//模擬耗時操作

                  NSLog(@"2-----%@",[NSThread currentThread]);//打印當前線程

              });

          });

      }

       6.GCD的其他方法

       6.1 GCD柵欄方法:dispatch_barrier_async

        

      /*

      柵欄方法

       */

      -(void)barrier{

          

          dispatch_queue_t queue = dispatch_queue_create("net.nange.test", DISPATCH_QUEUE_CONCURRENT);

          

          dispatch_async(queue, ^{

           

           //      異步 追加任務1

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"1----%@",[NSThread currentThread]); //打印當前線程

              }

           });

       

                         

              dispatch_async(queue, ^{

              

              //      異步 追加任務2

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"2----%@",[NSThread currentThread]); //打印當前線程

              }

         });

                         

                         

              dispatch_barrier_async(queue, ^{

                  

      //           追加任務barrier

                  for(int i=0;i<2;i++){

                      

                      [NSThread sleepForTimeInterval:2];

                      NSLog(@"barrier-----%@",[NSThread currentThread]);

                  }

              });

          

          

          dispatch_async(queue, ^{

              

              //      異步 追加任務3

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"3----%@",[NSThread currentThread]); //打印當前線程

              }

          });

          dispatch_async(queue, ^{

              

              //      異步 追加任務4

              for (int i=0; i<2; ++i) {

                  

                  [NSThread sleepForTimeInterval:2];  //模擬耗時操作

                  NSLog(@"4----%@",[NSThread currentThread]); //打印當前線程

              }

          });

       

      }

         6.2 GCD延時執行方法:dispatch_after

         指定時間(5秒)之后執行某個任務??梢杂肎CD的dispatch_after函數實現。

      需要注意的是:dispatch_after函數并不是在指定時間之后才開始處理,而是在指定時間之后將任務追加到隊列中。嚴格來說,這個時間并不是絕對準確的,但想要大致延遲執行任務,dispatch_after函數是很有效的。

         

      /*

      延時執行方法  diapatch_after

       */

      -(void)after{

       

          NSLog(@"currerntThread---%@",[NSThread currentThread]);//打印當前線程

          NSLog(@"asyncMain--begin");

          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{

              

              // 2.0秒后異步追加任務代碼到主隊列,并開始執行

              NSLog(@"after----%@",[NSThread currentThread]);//打印當前線程

          });

      }

        6.3 GCD 一次性代碼(只執行一次):dispatch_once

              我們在創建單例、或者有整個程序運行過程中只執行一次的代碼時,我們就用到了GCD的dispatch_once函數。使用dispatch_once函數能保證某段代碼在程序運行過程中只被執行一次,并且即使在多線程的環境下,dispatch_once也可以保證線程的安全。

        

      /*

      一次性代碼(只執行一次)dispatch_once

       */

      -(void)once{

       

          static dispatch_once_t onceToken;

          

          dispatch_once(&onceToken, ^{

             

              //只執行一次的代碼(這里面默認是線程安全的)

          });

      }

        6.4 GCD快速迭代方法:dispatch_apply

            通常我們會用for循環,但是GCD給我們提供了快速迭代的函數dispatch_apply.dispatch_apply按照指定的次數將指定的任務追加到指定的隊列中,并等待全部隊列執行結束。

         

      /*

      快速迭代方法 dispatch_apply

       */

      -(void)apply{

       

      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

          NSLog(@"apply----begin");

          

          dispatch_apply(6, queue, ^(size_t index) {

              

              NSLog(@"%zd-----%@",index,[NSThread currentThread]);

          });

          NSLog(@"apply----end");

      }

       

         6.5 GCD隊列組:dispatch_group

         需求:分別異步執行2個耗時任務,然后當2個耗時任務都執行完畢后再回到主線程執行任務。這時候我們可以使用到GCD的隊列組

        6.5.1 dispatch_group_notify

            監聽group中任務的完成狀態,當所有的任務都快執行完成后,追加任務到group中,并執行任務

        

      /*

      隊列組   dispatch_group_notify

       */

      -(void)groupNotify{

       

          NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程

          NSLog(@"group---begin");

          

          dispatch_group_t group =  dispatch_group_create();

          

          dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

              // 追加任務1

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程

              }

          });

          

          dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

              // 追加任務2

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程

              }

          });

          

          dispatch_group_notify(group, dispatch_get_main_queue(), ^{

              // 等前面的異步任務1、任務2都執行完畢后,回到主線程執行下邊任務

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程

              }

              NSLog(@"group---end");

          });

      }

       6.5.2 dispat_group_wait

        暫停當前線程(阻塞當前線程),等待指定的group中的任務執行完成后,才會往下繼續執行

      /**

       * 隊列組 dispatch_group_wait

       */

      - (void)groupWait {

          

          NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程

          NSLog(@"group---begin");

          

          dispatch_group_t group =  dispatch_group_create();

          

          dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

              // 追加任務1

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程

              }

          });

          

          dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

              // 追加任務2

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程

              }

          });

          

          // 等待上面的任務全部完成后,會往下繼續執行(會阻塞當前線程)

          dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

          

          NSLog(@"group---end");

      }

         6.5.3 dispatch_group_enter、dispatch_group_leave

        dispatch_group_enter標志著一個任務追加到 group,執行一次,相當于 group 中未執行完畢任務數+1

       dispatch_group_leave標志著一個任務離開了 group,執行一次,相當于 group 中未執行完畢任務數-1。

       當 group 中未執行完畢任務數為0的時候,才會使dispatch_group_wait解除阻塞,以及執行追加到dispatch_group_notify中的任務。

      /**

       * 隊列組 dispatch_group_enter、dispatch_group_leave

       */

      - (void)groupEnterAndLeave

      {

          NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程

          NSLog(@"group---begin");

          

          dispatch_group_t group = dispatch_group_create();

          dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

          dispatch_group_enter(group);

          dispatch_async(queue, ^{

              // 追加任務1

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程

              }

              dispatch_group_leave(group);

          });

          

          dispatch_group_enter(group);

          dispatch_async(queue, ^{

              // 追加任務2

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程

              }

              dispatch_group_leave(group);

          });

          

          dispatch_group_notify(group, dispatch_get_main_queue(), ^{

              // 等前面的異步操作都執行完畢后,回到主線程.

              for (int i = 0; i < 2; ++i) {

                  [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

                  NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程

              }

              NSLog(@"group---end");

          });

          //    // 等待上面的任務全部完成后,會往下繼續執行(會阻塞當前線程)

          //    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

          //

          //    NSLog(@"group---end");

      }

          6.6 GCD信號量 :dispatch_semaphore 
          GCD中的信號量是指Dispatch Semaphore,是持有計數的信號。類似于過過高速路收費站的欄桿??梢酝ㄟ^時,打開欄桿,不可以通過時,關閉欄桿。在Dispatch Semaphore中,使用計數來完成這個功能,計數為0時等待,不可通過,計數為1或大于1時,計數減1且不等待,可通過
         

      Dispatch Semaphore提供了三個函數。

      dispatch_semaphore_create:創建一個Semaphore并初始化信號的總量

      dispatch_semaphore_signal:發送一個信號,讓信號總量加1

      dispatch_semaphore_wait:可以使總信號量減1,當信號總量為0時就會一直等待(阻塞所在線程),否則就可以正常執行。

      注意:信號量的使用前提是:想清楚你需要處理哪個線程等待(阻塞),又要哪個線程繼續執行,然后使用信號量。

      Dispatch Semaphore 在實際開發中主要用于:

      • 保持線程同步,將異步執行任務轉換為同步執行任務
      • 保證線程安全,為線程加鎖
      6.61 Dispatch Semaphore 線程同步
           需求:異步執行耗時任務,并使用異步執行的結果進行一些額外的操作。換句話說,相當于,將異步執行任務轉換為同步執行任務。比如說:AFNetworking中AFURLSessionManager.m里面的taskForKeypath:方法。通過引入信號量的方式,等待異步執行任務結果,獲取到tasks,然后再返回該tasks.
          

      - (NSArray *)tasksForKeyPath:(NSString *)keyPath {

          __block NSArray *tasks = nil;

          dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

          [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {

              if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {

                  tasks = dataTasks;

              } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {

                  tasks = uploadTasks;

              } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {

                  tasks = downloadTasks;

              } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {

                  tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];

              }

              

              dispatch_semaphore_signal(semaphore);

          }];

          

          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

          

          return tasks;

      }

      接下來,我們利用Dispatch Semaphore實現線程同步,將異步執行任務轉換為同步執行任務。

      /**

       * semaphore 線程同步

       */

      - (void)semaphoreSync {

          

          NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程

          NSLog(@"semaphore---begin");

          

          dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

          dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

          

          __block int number = 0;

          dispatch_async(queue, ^{

              // 追加任務1

              [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

              NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程

              

              number = 100;

              

              dispatch_semaphore_signal(semaphore);

          });

          

          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

          NSLog(@"semaphore---end,number = %d",number);

      }

       6.6.2 Dispatch Semaphore線程安全和線程同步(為線程加鎖)

              線程安全:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行結果一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的

      若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作(更改變量),一般都需要考慮線程同步,否則的話就可能影響線程安全。

      線程同步:可理解為線程 A 和 線程 B 一塊配合,A 執行到一定程度時要依靠線程 B 的某個結果,于是停下來,示意 B 運行;B 依言執行,再將結果給 A;A 再繼續操作。

      舉個簡單例子就是:兩個人在一起聊天。兩個人不能同時說話,避免聽不清(操作沖突)。等一個人說完(一個線程結束操作),另一個再說(另一個線程再開始操作)。

      下面,我們模擬火車票售賣的方式,實現 NSThread 線程安全和解決線程同步問題。

      場景:總共有50張火車票,有兩個售賣火車票的窗口,一個是北京火車票售賣窗口,另一個是上?;疖嚻笔圪u窗口。兩個窗口同時售賣火車票,賣完為止。

          6.6.2.1  非線程安全 (不使用semaphore)

      /**

       * 非線程安全:不使用 semaphore

       * 初始化火車票數量、賣票窗口(非線程安全)、并開始賣票

       */

      - (void)initTicketStatusNotSave {

          NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程

          NSLog(@"semaphore---begin");

          

          self.ticketSurplusCount = 50;

          

          // queue1 代表北京火車票售賣窗口

          dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);

          // queue2 代表上?;疖嚻笔圪u窗口

          dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);

          

          __weak typeof(self) weakSelf = self;

          dispatch_async(queue1, ^{

              [weakSelf saleTicketNotSafe];

          });

          

          dispatch_async(queue2, ^{

              [weakSelf saleTicketNotSafe];

          });

      }

       

      /**

       * 售賣火車票(非線程安全)

       */

      - (void)saleTicketNotSafe {

          while (1) {

              

              if (self.ticketSurplusCount > 0) {  //如果還有票,繼續售賣

                  self.ticketSurplusCount--;

                  NSLog(@"%@", [NSString stringWithFormat:@"剩余票數:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);

                  [NSThread sleepForTimeInterval:0.2];

              } else { //如果已賣完,關閉售票窗口

                  NSLog(@"所有火車票均已售完");

                  break;

              }

              

          }

      }

      可以看到在不考慮線程安全,不使用 semaphore 的情況下,得到票數是錯亂的,這樣顯然不符合我們的需求,所以我們需要考慮線程安全問題。

       6.6.2.2 線程安全(使用semaphore加鎖)

          考慮線程安全的代碼

          

      /**

       * 線程安全:使用 semaphore加鎖

       * 初始化火車票數量、賣票窗口(線程安全)、并開始賣票

       */

      - (void)initTicketStatusNotSave {

          NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程

          NSLog(@"semaphore---begin");

          

         semaphoreLock = dispatch_semaphore_create(1);

          self.ticketSurplusCount = 50;

          

          // queue1 代表北京火車票售賣窗口

          dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);

          // queue2 代表上?;疖嚻笔圪u窗口

          dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);

          

          __weak typeof(self) weakSelf = self;

          dispatch_async(queue1, ^{

              [weakSelf saleTicketNotSafe];

          });

          

          dispatch_async(queue2, ^{

              [weakSelf saleTicketNotSafe];

          });

      }

       

      /**

       * 售賣火車票(非線程安全)

       */

      - (void)saleTicketNotSafe {

          while (1) {

              // 相當于加鎖

              dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);

              if (self.ticketSurplusCount > 0) {  //如果還有票,繼續售賣

                  self.ticketSurplusCount--;

                  NSLog(@"%@", [NSString stringWithFormat:@"剩余票數:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);

                  [NSThread sleepForTimeInterval:0.2];

              } else { //如果已賣完,關閉售票窗口

                  NSLog(@"所有火車票均已售完");

                  

                  // 相當于解鎖

                  dispatch_semaphore_signal(semaphoreLock);

                  break;

              }

              

              // 相當于解鎖

              dispatch_semaphore_signal(semaphoreLock);

          }

      }

       

      posted on 2019-03-13 11:16  強者VS弱者  閱讀(165)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 欧美福利电影A在线播放| 一边添奶一边添p好爽视频| 国产精品午夜福利免费看| 国产女人18毛片水真多1| 欧美黑人巨大xxxxx| 久久人妻精品白浆国产| 99精品国产精品一区二区| 中文文字幕文字幕亚洲色| 中文字幕精品人妻丝袜| 综合在线 亚洲 成人 欧美| 极品人妻少妇一区二区| 1区2区3区4区产品不卡码网站| 亚洲性一交一乱一伦视频| 成人av午夜在线观看| 精品少妇人妻av无码专区| 久女女热精品视频在线观看| 人人色在线视频播放| 久久综合亚洲鲁鲁九月天| 五月婷久久麻豆国产| 成人av午夜在线观看| 一级女性全黄久久生活片| 欧美日本精品一本二本三区| 国产精品七七在线播放| 亚洲欧美国产日韩天堂区| 国产一区二区三区乱码在线观看| 亚洲第一狼人天堂网伊人| 国产成人不卡一区二区| 国产精品亚洲二区亚瑟| 加勒比中文字幕无码一区| 激情97综合亚洲色婷婷五| 国产精品亚洲二区在线播放| 98日韩精品人妻一二区| 性色av蜜臀av色欲av| 97一期涩涩97片久久久久久久| 中文字幕一区二区网站| 四虎成人精品永久网站| 五月婷之久久综合丝袜美腿| 欧美另类videossexo高潮| 国产又爽又黄又无遮挡的激情视频| 中文字幕人妻精品在线| 中文字幕人妻精品在线|