線程控制:GCD開發(fā)常用總結(jié)梳理

一、相關(guān)概念

GCD全稱Grand Central Dispatch,屬于系統(tǒng)級的線程管理,通過 GCD,我們可以對當前程序執(zhí)行的線程進行調(diào)度與控制,而不需要過度關(guān)注線程創(chuàng)建釋放相關(guān)內(nèi)容,這無疑大大節(jié)約了開發(fā)的精力,并且將繁瑣的線程抽象起來,更有利于掌握和理解;

  • 串行(Serial):讓任務一個接著一個地執(zhí)行(一個任務執(zhí)行完畢后,再執(zhí)行下一個任務)
  • 并發(fā)(Concurrent):可以讓多個任務并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務)并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效。
  • 同步(Synchronous):在當前線程中執(zhí)行任務,不具備開啟新線程的能力
  • 異步(Asynchronous):在新的線程中執(zhí)行任務,具備開啟新線程的能力

二、舉例說明

沒有什么比直接使用例子更通俗易懂:

隊列

分為串行隊列與并行隊列,執(zhí)行分為異步與同步

    //串行隊列的創(chuàng)建方法
    dispatch_queue_t queueSerial = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    //并發(fā)隊列的創(chuàng)建方法
    dispatch_queue_t queueC = dispatch_queue_create("conTest.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"asyncConcurrent---begin");
    //同步執(zhí)行任務創(chuàng)建方法
    dispatch_sync(queueC, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1---sync---%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queueC, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2---sync---%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queueC, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3---sync---%@",[NSThread currentThread]);
        }
    });
    
    //異步執(zhí)行任務創(chuàng)建方法
    dispatch_async(queueC, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queueC, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queueC, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"syncConcurrent---end");
    
    //并發(fā)同步隊列在一個線程中執(zhí)行,并發(fā)異步隊列則由系統(tǒng)分配的線程執(zhí)行,執(zhí)行速度不一定比當前線程的速度慢

主線程與線程切換


  dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"asyncMain---begin");
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_block_t block = ^{
        NSLog(@"block------%@",[NSThread currentThread]);
        NSLog(@"new block message");
    };
    
    dispatch_async(queue, block);
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"asyncMain---end");
    //在指定線程中執(zhí)行的異步操作,遵循代碼執(zhí)行順序,碰到異步的函數(shù)塊,即拋到線程最后排隊;

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async( dispatch_get_main_queue(), ^{
            
        });
    });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 2; ++i) {
                NSLog(@"1------%@",[NSThread currentThread]);
            }
            //回到主線程
            dispatch_async(dispatch_get_main_queue(), ^{
                for (int i = 0; i < 2; ++i) {
                    NSLog(@"2------%@",[NSThread currentThread]);
                }
            });
        });

dispatch_once用法

// 使用dispatch_once構(gòu)建單例,可以保證單例線程安全
+ (instancetype)shareInstance {
    static PhotoHandler *handler = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        handler = [[PhotoHandler alloc] init];
    });
    return handler;
}

dispatch_barrier 與dispatch_group 及 dispatch_apply 常用方法及區(qū)別

dispatch_group在日常開發(fā)中用處可能更大一點

 dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //執(zhí)行耗時操作1
        NSLog(@"執(zhí)行耗時操作1");
         NSLog(@"執(zhí)行耗時操作1------%@",[NSThread currentThread]);
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (long i = 0 ; i < 7000000; i ++) {
                long j = i;
            }
            NSLog(@"內(nèi)部在異步1");
            NSLog(@"內(nèi)部在異步1------%@",[NSThread currentThread]);
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"內(nèi)部在異步3");
            NSLog(@"內(nèi)部在異步3------%@",[NSThread currentThread]);
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                sleep(1); //這里線程睡眠1秒鐘,模擬異步請求
                NSLog(@"內(nèi)部在異步2");
                NSLog(@"內(nèi)部在異步2------%@",[NSThread currentThread]);
                
            });
        });
        
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //執(zhí)行耗時操作2
        NSLog(@"執(zhí)行耗時操作2------%@",[NSThread currentThread]);
        NSLog(@"執(zhí)行耗時操作2");
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //執(zhí)行耗時操作3
        NSLog(@"執(zhí)行耗時操作3------%@",[NSThread currentThread]);
        NSLog(@"執(zhí)行耗時操作3");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"前面的異步操作已完成");
    });
    
    //dispatch_group_create()可以創(chuàng)建一個完全的線程控制,這這個group中的線程,無論該線程是否新開異步線程,
    //dispatch_group_notify都會在該group線程所有內(nèi)容執(zhí)行完成以后,再執(zhí)行相關(guān)內(nèi)容(但是sleep(1); 模擬異步請求,dispatch_group_notify不會等待 如果要精確確定異步的執(zhí)行完畢,可以用dispatch_group_enter, dispatch_group_leave來管理)
    //所謂異步執(zhí)行就是將當前在異步執(zhí)行的代碼以函數(shù)塊形式排隊放到線程(系統(tǒng)分配的線程,不一定是目前執(zhí)行的線程)執(zhí)行的最后
    //由于執(zhí)行的線程不一致,所以完成先后順序也不一致
//對于內(nèi)部再次異步的的內(nèi)容(如網(wǎng)絡請求),group并不能保證獲取結(jié)果以后再執(zhí)行通知

dispatch_barrier 與dispatch_apply

   //GCD的快速迭代方法
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    /*! dispatch_apply函數(shù)說明
           *
           *  @brief  dispatch_apply函數(shù)是dispatch_sync函數(shù)和Dispatch Group的關(guān)聯(lián)API
           *         該函數(shù)按指定的次數(shù)將指定的Block追加到指定的Dispatch Queue中,并等到全部的處理執(zhí)行結(jié)束
           *
           *  @param 6    指定重復次數(shù)  指定6次
           *  @param queue 追加對象的Dispatch Queue
           *  @param index 帶有參數(shù)的Block, index的作用是為了按執(zhí)行的順序區(qū)分各個Block
           *
          */
    
    dispatch_apply(6, globalQueue, ^(size_t index) {
        NSLog(@"%zd---globalQueue---%@",index, [NSThread currentThread]);
    });
    
    dispatch_queue_t queue = dispatch_queue_create("ssss", DISPATCH_QUEUE_CONCURRENT);
    
    
    
   
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"內(nèi)部在異步1");
            NSLog(@"內(nèi)部在異步1------%@",[NSThread currentThread]);
            
        });
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"內(nèi)部在異步2");
            NSLog(@"內(nèi)部在異步2------%@",[NSThread currentThread]);
            
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 2; ++i) {
                NSLog(@"內(nèi)部queue------%@",[NSThread currentThread]);
            }
        });
    });
    //先執(zhí)行完柵欄前面的在執(zhí)行后面的
    dispatch_barrier_sync(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
    
        /**
         1.柵欄操作時候,只能攔截該線程中第一層異步操作的內(nèi)容,對第一層中再次異步操作的線程無法攔截,同時屬于這個線程的也不行
         */
    
    
    
//    dispatch_async(queue, ^{
//        for (int i = 0; i < 2; ++i) {
//            NSLog(@"3------%@",[NSThread currentThread]);
//        }
//    });
//    dispatch_async(queue, ^{
//        for (int i = 0; i < 2; ++i) {
//            NSLog(@"4------%@",[NSThread currentThread]);
//        }
//    });
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"only once");
    });
    //延時執(zhí)行,不受柵欄的影響
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"run -----");
    });

死鎖相關(guān)情形

-(void)deadThread {
    NSLog(@"=================4");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"=================5");
    });
    NSLog(@"=================6");
}

-(void)deadThread2 {
    dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); // 任務1
    dispatch_async(queue, ^{
        NSLog(@"2"); // 任務2
        dispatch_sync(queue, ^{
            NSLog(@"3"); // 任務3
        });
        NSLog(@"4"); // 任務4
    });
    NSLog(@"5"); // 任務5
/**
 執(zhí)行任務1;
 遇到異步線程,將【任務2、同步線程、任務4】加入串行隊列中。因為是異步線程,所以在主線程中的任務5不必等待異步線程中的所有任務完成;
 因為任務5不必等待,所以2和5的輸出順序不能確定;
 任務2執(zhí)行完以后,遇到同步線程,這時,將任務3加入串行隊列;
 又因為任務4比任務3早加入串行隊列,所以,任務3要等待任務4完成以后,才能執(zhí)行。但是任務3所在的同步線程會阻塞,所以任務4必須等任務3執(zhí)行完以后再執(zhí)行。這就又陷入了無限的等待中,造成死鎖。
 */
}

semaphore信號量的應用

信號量使用的主要三個函數(shù)

//創(chuàng)建信號量,參數(shù):信號量的初值,如果小于0則會返回NULL
dispatch_semaphore_create(信號量值)
 
//等待降低信號量
dispatch_semaphore_wait(信號量,等待時間)
 
//提高信號量
dispatch_semaphore_signal(信號量)

其實,這有點類似鎖機制了,只不過信號量都是系統(tǒng)幫助我們處理了,我們只需要在執(zhí)行線程之前,設(shè)定一個信號量值(這個值必須是大于或者等于0),關(guān)于這個值的理解我們可以從下面兩個例子中獲取比較準確的定義;

一、模擬多線程操作時幾個任務同時進行,所有結(jié)果完成后才進行處理
    dispatch_semaphore_t semaphoreControl = dispatch_semaphore_create(0);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSLog(@"hoggen run task 1");
        sleep(1);
        NSLog(@"hoggen complete task 1");
        dispatch_semaphore_signal(semaphoreControl);
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"hoggen run task 2");
        sleep(2);
        NSLog(@"hoggen complete task 2");
        dispatch_semaphore_signal(semaphoreControl);
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"hoggen run task 3");
        sleep(3);
        NSLog(@"hoggen complete task 3");
        dispatch_semaphore_signal(semaphoreControl);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"hoggen run task 4");
        sleep(4);
        NSLog(@"hoggen complete task 4");
        dispatch_semaphore_signal(semaphoreControl);
    });
    
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
        NSLog(@"=================模擬多線程操作時幾個任務同時進行,完成后才輸出結(jié)果======================");
    });
二、用于多線程,線程數(shù)量的控制
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
        dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //任務1
        dispatch_async(queue2, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 1");
            sleep(1);
            NSLog(@"complete task 1");
            dispatch_semaphore_signal(semaphore);
        });
        //任務2
        dispatch_async(queue2, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 2");
            sleep(1);
            NSLog(@"complete task 2");
            dispatch_semaphore_signal(semaphore);
        });
        //任務3
        dispatch_async(queue2, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 3");
            sleep(1);
            NSLog(@"complete task 3");
            dispatch_semaphore_signal(semaphore);
        });

輸出結(jié)果為:

 =================semaphore信號量測試======================
 hoggen run task 2
 hoggen run task 1
 hoggen run task 3
hoggen run task 4
 run task 1
 run task 2
 hoggen complete task 1
complete task 1
complete task 2
 run task 3
 hoggen complete task 2
complete task 3
hoggen complete task 3
 hoggen complete task 4
=================模擬多線程操作時幾個任務同時進行,完成后才輸出結(jié)果======================

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容