1. 程式人生 > >07.GCD函式和佇列組合示例

07.GCD函式和佇列組合示例

1.GCD基本使用(非同步函式+併發佇列)

  • 可以看到,系統自動給開闢了新的子執行緒來執行這三個任務,且由於時併發佇列,系統開闢了三個子執行緒
  • 注意:開闢多少個子執行緒由系統決定,三個任務依次從佇列中取出放在不同的子執行緒執行,但是由於CPU排程子執行緒是由系統控制的,所以任務執行的順序不定,並沒有違背佇列中任務的先進先出原則
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

    // 1.建立佇列
    /* 引數解釋
     第一個引數:佇列名稱
     第二個引數:佇列屬性(序列還是併發)
     DISPATCH_QUEUE_SERIAL :序列
     DISPATCH_QUEUE_CONCURRENT 併發
     */
// 1.1建立一個併發佇列 // dispatch_queue_t queue = dispatch_queue_create("zj.queue", DISPATCH_QUEUE_CONCURRENT); // 1.2系統內部已經給我們提供好了一個現成的併發佇列 /* 第一個引數: iOS8以前是優先順序, iOS8以後是服務質量 iOS8以前 * - DISPATCH_QUEUE_PRIORITY_HIGH 高優先順序 2 * - DISPATCH_QUEUE_PRIORITY_DEFAULT: 預設的優先順序 0 * - DISPATCH_QUEUE_PRIORITY_LOW: 低優先順序 -2 * - DISPATCH_QUEUE_PRIORITY_BACKGROUND: 後臺 iOS8以後 * - QOS_CLASS_USER_INTERACTIVE 0x21 使用者互動(使用者迫切想執行任務) * - QOS_CLASS_USER_INITIATED 0x19 使用者需要 * - QOS_CLASS_DEFAULT 0x15 預設 * - QOS_CLASS_UTILITY 0x11 工具(低優先順序, 蘋果推薦將耗時操作放到這種型別的佇列中) * - QOS_CLASS_BACKGROUND 0x09 後臺 * - QOS_CLASS_UNSPECIFIED 0x00 沒有設定 第二個引數: Flags that are reserved for future use. Always specify 0 for this parameter. 根據蘋果文件,可以知道這是一個保留值,以便蘋果在系統內部執行一些操作,並且蘋果建議我們始終傳入0即可. */
// 為了同時適配iOS8以及iOS8以前的版本,優先順序引數直接傳0即可 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 2.新增任務到佇列(這裡使用非同步方式) /* 引數解釋 第一個引數:需要將任務新增到哪個佇列 第二個引數:需要執行的任務 */ // 連續新增三個任務 dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); }); dispatch_async
(queue, ^{ NSLog(@"%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); }); /* 結果:(每次列印的執行緒number順序不一致) <NSThread: 0x7fb878d02110>{number = 4, name = (null)} <NSThread: 0x7fb878e21500>{number = 3, name = (null)} <NSThread: 0x7fb878f91e20>{number = 2, name = (null)} */ }

2.GCD基本使用(非同步函式+序列佇列)

  • 可以看到,開啟了新的執行緒,但是隻開啟了一條
  • 能夠建立新執行緒的原因:使用”非同步”函式呼叫
  • 只建立1個子執行緒的原因:佇列是序列佇列
  • 由於佇列序列佇列,所以任務是從上到下依次執行
  • 由於使用了非同步函式,那麼不會等到非同步函式中的任務執行完畢再去執行後面的程式碼
- (void)asynSerial{
    // 1.建立序列佇列
    dispatch_queue_t queue = dispatch_queue_create("zj.queue", DISPATCH_QUEUE_SERIAL);
    // 2.新增任務到佇列
    // 連續新增三個任務
    dispatch_async(queue, ^{
        NSLog(@"任務一執行,%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務二執行,%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務三執行,%@",[NSThread currentThread]);
    });
    NSLog(@"+++++++");
    /* 列印結果:
     任務一執行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
     +++++++
     任務二執行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
     任務三執行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
     */
}

3.GCD基本使用(同步函式+序列佇列)

  • 可以看到不會開啟新的執行緒,且執行緒中的任務會依次執行
  • 因為呼叫了同步函式, 那麼會等同步函式中的任務執行完畢, 才會執行後面的程式碼

- (void)syncSerial
{
    // 1.建立一個序列佇列
    // #define DISPATCH_QUEUE_SERIAL NULL
    // 所以可以直接傳NULL
    dispatch_queue_t queue = dispatch_queue_create("zj.queue", NULL);

    // 2.將任務新增到佇列中
    dispatch_sync(queue, ^{
        NSLog(@"任務1  == %@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任務2  == %@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任務3  == %@", [NSThread currentThread]);
    });

    NSLog(@"---------");
    /*輸出結果:
     任務1  == <NSThread: 0x7fe129512690>{number = 1, name = main}
     任務2  == <NSThread: 0x7fe129512690>{number = 1, name = main}
     任務3  == <NSThread: 0x7fe129512690>{number = 1, name = main}
     ---------
     */
}

4.GCD基本使用(同步函式+併發佇列)

  • 使用了同步函式,不會建立新的執行緒
/*
 同步 + 併發 : 不會開啟新的執行緒
 */
- (void)syncConCurrent
{
    // 1.建立一個併發佇列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 2.將任務新增到佇列中
    dispatch_sync(queue, ^{
        NSLog(@"任務1  == %@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任務2  == %@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任務3  == %@", [NSThread currentThread]);
    });
    NSLog(@"---------");
    /*輸出結果:
     任務1  == <NSThread: 0x7fe129512690>{number = 1, name = main}
     任務2  == <NSThread: 0x7fe129512690>{number = 1, name = main}
     任務3  == <NSThread: 0x7fe129512690>{number = 1, name = main}
     ---------
     */
}

5.GCD基本使用(非同步函式+主佇列)

  • 只要是在主佇列,那麼無論是呼叫同步還是非同步函式,都不會開啟新執行緒,一定會在主執行緒中執行
/*
 非同步 + 主佇列 : 不會建立新的執行緒,並且任務是在主執行緒中執行
 */
- (void)asyncMain
{
    // 建立主佇列
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
}
    /* 輸出結果:
     任務一 <NSThread: 0x7fa1d35267c0>{number = 1, name = main}
     任務二 <NSThread: 0x7fa1d35267c0>{number = 1, name = main}
     */

6.GCD基本使用(同步函式+主佇列)-死鎖

  • 如果是在主執行緒中呼叫同步函式 + 主佇列, 那麼會導致死鎖
    導致死鎖的原因:
    • sync函式是在主執行緒中執行的, 並且會等待block執行完畢. 先呼叫
    • block是新增到主佇列的, 也需要在主執行緒中執行. 後呼叫
    • 由於佇列的先進先出原則,那麼sync函式要先執行,但是他又要等待block執行完畢才會往後繼續執行,block又在主佇列的後面,等待同步函式執行完畢後才會執行,所以兩個任務相互等待,永遠都不會往下繼續執行,造成死鎖
/*
 在主執行緒中呼叫同步函式+主佇列會導致死鎖
 */
- (void)syncMain
{
    NSLog(@"%@", [NSThread currentThread]);
    // 主佇列:
    dispatch_queue_t queue = dispatch_get_main_queue();
    //  如果是呼叫 同步函式, 那麼會等同步函式中的任務執行完畢, 才會執行後面的程式碼
    // 注意: 如果dispatch_sync方法是在主執行緒中呼叫的, 並且傳入的佇列是主佇列, 那麼會導致死鎖
    dispatch_sync(queue, ^{
        NSLog(@"----------");
        NSLog(@"%@", [NSThread currentThread]);
    });
    NSLog(@"----------");
}
/* 輸出結果:佇列中的任務以及主執行緒中後續的操作不再執行
<NSThread: 0x7fadbad1ba70>{number = 1, name = main}
*/

7.GCD基本使用(同步函式+主佇列)-不死鎖

  • 要想使用同步函式和主佇列,且不造成死鎖,可以在子執行緒中執行同步函式
/*
 如果是在子執行緒中呼叫 同步函式 + 主佇列, 那麼沒有任何問題
 */
- (void)syncMain2
{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        // block會在子執行緒中執行
        //        NSLog(@"%@", [NSThread currentThread]);

        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_sync(queue, ^{
            // block一定會在主執行緒執行
            NSLog(@"%@", [NSThread currentThread]);
        });
    });
    NSLog(@"------------");
    /* 輸出結果:
     ------------
     <NSThread: 0x7fd16af280d0>{number = 1, name = main}
     */
}