GCD死鎖,及同步、非同步、串列和並行隊列組合情形

来源:https://www.cnblogs.com/lybSkill/archive/2019/02/14/10375404.html
-Advertisement-
Play Games

一、概述 1)隊列用來存儲代碼任務,線程用來運行代碼任務; 2)main()函數作為程式入口,整個程式預設運行在主線程中,程式代碼任務預設存放在主隊列中; 3)以下所謂阻塞線程是針對主線程而言(子線程阻塞在所不問,自己手動管理);隊列阻塞主要是針對主隊列(子隊列阻塞在所不問,自己手動管理); 4)在 ...


  一、概述 1)隊列用來存儲代碼任務,線程用來運行代碼任務; 2)main()函數作為程式入口,整個程式預設運行在主線程中,程式代碼任務預設存放在主隊列中; 3)以下所謂阻塞線程是針對主線程而言(子線程阻塞在所不問,自己手動管理);隊列阻塞主要是針對主隊列(子隊列阻塞在所不問,自己手動管理); 4)線上程中(如:主線程)添加block任務(以下簡稱“B”)到某個隊列中,添加B本身也是一個任務即dispatch_sync或async代碼本身也由當前線程(如:主線程)來運行(以下簡稱“A”)——簡言之,代碼就是任務,任務就是代碼(所謂的block任務,實質就是block大括弧裡面的代碼); 5)隊列分三種:主隊列、自定義串列隊列(以下簡稱“自定串”)、並行隊列; 6)線程阻塞原理(以主隊列為例):系統會從主隊列中按順序取出某個任務放到主線程中運行,一旦該任務運行完畢,該任務即從主隊列中結束銷毀,系統就會自動從主隊列中取出下一個任務又放到主線程中運行(相當於前一個),如此反覆,直到主隊列中的任務全部運行完畢;   二、非同步併發   1)示例圖   2)代碼
dispatch_queue_t queue2 = dispatch_get_global_queue(0, 0);

dispatch_async(queue2, ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"執行任務3-----%@", [NSThread currentThread]);
        }
    });

for (int i = 0; i < 10; i++) {
        NSLog(@"執行任務1-----%@", [NSThread currentThread]);
    }

//列印

2019-02-14 10:33:28.408678+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.408685+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-14 10:33:28.408935+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.408935+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-14 10:33:28.409076+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.409098+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-14 10:33:28.409185+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.409232+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-14 10:33:28.409301+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.409618+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-14 10:33:28.409873+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.410141+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-14 10:33:28.410480+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.410794+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-14 10:33:28.411167+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.411486+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
2019-02-2019-02-14 10:33:28.411962+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
14 10:33:28.411763+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.422231+0800 MJ_iOS_Test[1864:38570] 執行任務1-----<NSThread: 0x600000c16980>{number = 1, name = main}
2019-02-14 10:33:28.422250+0800 MJ_iOS_Test[1864:38623] 執行任務3-----<NSThread: 0x600000c7b780>{number = 3, name = (null)}
3)分析 <1>首先添加GCD中的block任務(即“執行任務3”,相當於B)到併發隊列queue2中,添加本身也是一個任務即A,該任務是存儲在主隊列中,由主線程運行; <2>B被添加到queue2後,因為是非同步,此時A不會在主線程中等待B執行完畢,而是由系統自動返回到主隊列中取出下一個任務放到主線程中運行; 與此同時,系統又會單獨開闢一個子線程來運行queue2中的B,所以即可由子線程來運行B; <3>因此,此時系統有兩條線程(主線程:number為1,name為main;子線程:number為3,name為null——因為沒有取名字)在運行各自的任務,互不影響,交替執行;   三、幾種情形   1)同步串列——主隊列 結果:壞指令執行錯誤——死鎖   //代碼
dispatch_queue_t queue1 = dispatch_get_main_queue();

dispatch_sync(queue1, ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"執行任務3-----%@", [NSThread currentThread]);
        }
    });

 

//錯誤

    //分析 <1>A存儲在主隊列中,由主線來運行,B被添加到主隊列中後,因為是同步,系統不會開闢一個子線程來運行B; <2>A在主線程中運行完畢後,理應從主隊列中銷毀而執行下一個任務B,但因為是同步,A會依然停留在主隊列中等待B運行完畢而不會立即銷毀;又因為主隊列是一個串列隊列,因此B必須等到A執行完畢才能被系統取出放在主線程上運行(因為A排在B前面); <3>局面:A在主隊列中一直等待B的執行完畢,而B在主隊列中一直等待A的執行完畢——“你等我,我等你”,死鎖;     -------問題:死鎖僅限於主隊列嗎?   //代碼
dispatch_queue_t queue3 = dispatch_queue_create("myQueue3", DISPATCH_QUEUE_SERIAL);

NSLog(@"執行任務1----%@", [NSThread currentThread]);

    dispatch_async(queue3, ^{

        NSLog(@"執行任務2----%@", [NSThread currentThread]);

        dispatch_sync(queue3, ^{

            NSLog(@"執行任務3----%@", [NSThread currentThread]);

        });

        NSLog(@"執行任務4----%@", [NSThread currentThread]);

    });

    

    NSLog(@"執行任務5----%@", [NSThread currentThread]);

 

//列印

 

//分析  <1>queue3自定義串列隊列並且非同步執行,所以系統會開闢一個子線程(number為3);主隊列存儲1、5和第一個async對應的A(以下簡稱“A1”),共三個任務;queue3存儲2、3、4和第二個sync對應的A(以下簡稱“A2”),共四個任務; <2>首先系統從主隊列中取出1放到主線程上運行,待1運行完畢後(併在主隊列中自動銷毀),又取出A1放到主線程上運行,因為是非同步,A1並不需要停留在主隊列一直等待async中的block任務執行完畢,所以A1一執行完畢(併在主隊列中自動銷毀),系統又會從主隊列中取出5放到主線程上運行,與此同時系統又會新開闢一個子線程來運行async中的block任務,該任務中的第一個小任務就是2——因此,此刻系統同時在跑兩條線程,交替執行(誰先誰後由系統隨機決定,沒有定數); <3>執行完1、5後,主隊列中的非系統任務全部執行完畢,此時子線程運行完2後(併在queue3中自動銷毀),系統又會從queue3中取出A2放到子線程中運行,A2運行完畢時,3被添加到了queue3中,同時因為是同步,所以A2依然會停留在queue3中一直等待3運行完畢(而不會即可自動銷毀,只有等到3運行完畢時才會銷毀),又因為queue3是串列隊列,3排在A2後面一直在等待A2執行完畢——局面:A2與3相互等待,死鎖; <4>程式卡死在sync代碼處,其後的代碼(任務4)自然也就不能執行了;   所以,死鎖不限於主隊列,但限於串列隊列!!!     ------問題:如何解決死鎖?   方案一:將A2和3放在不同的隊列中   //代碼
dispatch_queue_t queue3 = dispatch_queue_create("myQueue3", DISPATCH_QUEUE_SERIAL);

    dispatch_queue_t queue4 = dispatch_queue_create("myQueue4", DISPATCH_QUEUE_SERIAL);

NSLog(@"執行任務1----%@", [NSThread currentThread]);

    dispatch_async(queue3, ^{

        NSLog(@"執行任務2----%@", [NSThread currentThread]);

        dispatch_sync(queue4, ^{
            NSLog(@"執行任務3----%@", [NSThread currentThread]);
        });

        NSLog(@"執行任務4----%@", [NSThread currentThread]);
    });

    NSLog(@"執行任務5----%@", [NSThread currentThread]);

//列印

2019-02-15 12:28:51.782328+0800 MJ_iOS_Test[3879:128281] 執行任務1----<NSThread: 0x600003661480>{number = 1, name = main}
2019-02-15 12:28:51.782639+0800 MJ_iOS_Test[3879:128281] 執行任務5----<NSThread: 0x600003661480>{number = 1, name = main}
2019-02-15 12:28:51.782678+0800 MJ_iOS_Test[3879:128333] 執行任務2----<NSThread: 0x600003607fc0>{number = 3, name = (null)}
2019-02-15 12:28:51.782818+0800 MJ_iOS_Test[3879:128333] 執行任務3----<NSThread: 0x600003607fc0>{number = 3, name = (null)}
2019-02-15 12:28:51.782955+0800 MJ_iOS_Test[3879:128333] 執行任務4----<NSThread: 0x600003607fc0>{number = 3, name = (null)}

//分析

<1>dispatch_sync本身運行在子線程中,而該子線程對應的隊列為queue3,因此A2存放在queue3中,而3存放在queue4中;

<2>當子線程運行完畢2後(並隨即自動銷毀),此時系統是從queue3中取出4還是從queue4取出3放到子線程中運行呢?——因為同步,系統不會開闢新的線程,添加的block任務依然運行在當前線程中(number為3的子線程);同時,A2會一直停留在queue3中,直到3執行完畢才會自動銷毀,又因為queue3是個串列隊列而4排在A2後面,所以A2把4給堵住了,當3運行完畢後,A2銷毀,系統就從queue3中取出4放到當前線程中運行;

註:此處將3放到一個併發隊列中,效果是一樣的;

  方案二:非同步添加3到queue3中   //代碼
dispatch_queue_t queue3 = dispatch_queue_create("myQueue3", DISPATCH_QUEUE_SERIAL);

NSLog(@"執行任務1----%@", [NSThread currentThread]);

    dispatch_async(queue3, ^{

        NSLog(@"執行任務2----%@", [NSThread currentThread]);

        dispatch_async(queue3, ^{
            NSLog(@"執行任務3----%@", [NSThread currentThread]);
        });

        NSLog(@"執行任務4----%@", [NSThread currentThread]);
    });

    NSLog(@"執行任務5----%@", [NSThread currentThread]);

//列印

2019-02-15 14:09:46.953030+0800 MJ_iOS_Test[5523:191263] 執行任務1----<NSThread: 0x6000030aa900>{number = 1, name = main}
2019-02-15 14:09:46.953330+0800 MJ_iOS_Test[5523:191263] 執行任務5----<NSThread: 0x6000030aa900>{number = 1, name = main}
2019-02-15 14:09:46.953326+0800 MJ_iOS_Test[5523:191330] 執行任務2----<NSThread: 0x6000030c2880>{number = 3, name = (null)}
2019-02-15 14:09:46.953608+0800 MJ_iOS_Test[5523:191330] 執行任務4----<NSThread: 0x6000030c2880>{number = 3, name = (null)}
2019-02-15 14:09:46.954050+0800 MJ_iOS_Test[5523:191330] 執行任務3----<NSThread: 0x6000030c2880>{number = 3, name = (null)}

//分析——我門只分析3和4的順序

<1>因為是非同步且queue3非主隊列,所以系統理應開闢一個新線程,但是運行完2後,此刻當前線程(number為3的子線程)處於閑置狀態,為了節省資源,系統並不會真的開闢一個新線程,所有後面的任務依然會運行在當前子線程中(除非當前線程正在運行中,而同時要非同步運行另一個任務,系統才會考慮開啟一個新線程,或者手動強行開啟一個新線程(非同步併發));

<2>首先,我們明確下A2、3和4在queue3中的存放順序:

因為3的存放是由A2線上程上運行完成的,而在編譯階段A2並不會運行,但此時系統會給A2和4分配位置——因此A2和4的存放是在編譯階段完成的,而3的存放是在運行階段完成的,所以如下圖所示

<3>因為非同步,所以A2不會停留在queue3中一直等待3執行完畢而是將3添加到queue3中隨即自動銷毀,因為queue3是個串列隊列,此時系統就從queue3中取出4放到當前子線程中運行,待4運行完畢,系統又會取出3放到當前子線程中運行;

說明:dispatch添加block任務(不論非同步還是同步),都是在當前線程上運行完成的,因此該block任務的存放位置一定在dispatch後面所有外部任務(後面dispatch中的block任務以此類推)的位置後面———這點,請註意!!!

  2)同步串列——自定義串列隊列 結果:順序執行   //代碼
dispatch_queue_t queue3 = dispatch_queue_create("myQueue3", DISPATCH_QUEUE_SERIAL);

dispatch_sync(queue3, ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"執行任務3-----%@", [NSThread currentThread]);
        }
    });

for (int i = 0; i < 10; i++) {
        NSLog(@"執行任務1-----%@", [NSThread currentThread]);
    }

//列印

2019-02-14 11:10:57.529267+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.529503+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.529627+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.529771+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.529903+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.530018+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.530147+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.530270+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.530418+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.530547+0800 MJ_iOS_Test[2440:59133] 執行任務3-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.530666+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.530898+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.531152+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.549535+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.549711+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.549853+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.549989+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.550113+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.550247+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
2019-02-14 11:10:57.550381+0800 MJ_iOS_Test[2440:59133] 執行任務1-----<NSThread: 0x60000252ce40>{number = 1, name = main}
//分析 <1>A存儲在主隊列中,由主線來運行,B被添加到自定義串列隊列queue3中;因為是同步,系統不會開闢一個子線程來運行B; <2>A在主線程運行完畢後會停留在主隊列中一直等待B執行完畢,此時系統無法從主隊列中取出下一個任務放到主線程上運行(因為被A堵住了),但會並且能從queue3中取出B放到主線程上運行; <3>當主線程在運行“執行任務3”時,線程阻塞,當“執行任務3”運行完畢時,A收到消息則銷毀,此時系統就會從主隊列中取出下一個任務“執行任務1”放到主線程上運行;   註:同樣是同步串列,為什麼此處就不會造成死鎖——因為,A和B存放在不同隊列中,不存在相互等待的局面;   3)同步並行 同“2)”;   //代碼
dispatch_queue_t queue2 = dispatch_get_global_queue(0, 0);

for (int i = 0; i < 5; i++) {
        NSLog(@"執行任務1----%@", [NSThread currentThread]);
    }
    
    dispatch_async(queue2, ^{

        for (int i = 0; i < 5; i++) {
            NSLog(@"執行任務2----%@", [NSThread currentThread]);
        }

        dispatch_sync(queue2, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"執行任務3----%@", [NSThread currentThread]);
            }
        });

        for (int i = 0; i < 5; i++) {
            NSLog(@"執行任務4----%@", [NSThread currentThread]);
        }
    });

    for (int i = 0; i < 5; i++) {
        NSLog(@"執行任務5----%@", [NSThread currentThread]);
    }

//列印

2019-02-15 17:19:50.417681+0800 MJ_iOS_Test[8640:311746] 執行任務1----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.417926+0800 MJ_iOS_Test[8640:311746] 執行任務1----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.418055+0800 MJ_iOS_Test[8640:311746] 執行任務1----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.418175+0800 MJ_iOS_Test[8640:311746] 執行任務1----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.418292+0800 MJ_iOS_Test[8640:311746] 執行任務1----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.418449+0800 MJ_iOS_Test[8640:311746] 執行任務5----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.418492+0800 MJ_iOS_Test[8640:311806] 執行任務2----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.418569+0800 MJ_iOS_Test[8640:311746] 執行任務5----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.418727+0800 MJ_iOS_Test[8640:311806] 執行任務2----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.419157+0800 MJ_iOS_Test[8640:311746] 執行任務5----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.419427+0800 MJ_iOS_Test[8640:311806] 執行任務2----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.419705+0800 MJ_iOS_Test[8640:311746] 執行任務5----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.419987+0800 MJ_iOS_Test[8640:311806] 執行任務2----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.420301+0800 MJ_iOS_Test[8640:311746] 執行任務5----<NSThread: 0x600002e96900>{number = 1, name = main}
2019-02-15 17:19:50.427851+0800 MJ_iOS_Test[8640:311806] 執行任務2----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.428027+0800 MJ_iOS_Test[8640:311806] 執行任務3----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.428232+0800 MJ_iOS_Test[8640:311806] 執行任務3----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.429049+0800 MJ_iOS_Test[8640:311806] 執行任務3----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.429279+0800 MJ_iOS_Test[8640:311806] 執行任務3----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.429431+0800 MJ_iOS_Test[8640:311806] 執行任務3----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.429774+0800 MJ_iOS_Test[8640:311806] 執行任務4----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.430498+0800 MJ_iOS_Test[8640:311806] 執行任務4----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.430812+0800 MJ_iOS_Test[8640:311806] 執行任務4----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.431099+0800 MJ_iOS_Test[8640:311806] 執行任務4----<NSThread: 0x600002ef8680>{number = 3, name = (null)}
2019-02-15 17:19:50.431531+0800 MJ_iOS_Test[8640:311806] 執行任務4----<NSThread: 0x600002ef8680>{number = 3, name = (null)}

//分析

<1>當主線程(以下簡稱“T0”)運行完1後,程式走到第一個非同步併發,此時系統必然開啟一個新線程(以下簡稱“T1”),該線程運行第一個大block中的內容(以下簡稱“B1”),而主線程繼續運行5,5和第一個大block中的內容的運行互不影響,交替執行,這點沒問題;

<2>但是,問題來了—————第一個大block中的內容要先執行哪一個呢?為什麼是2跟5交替執行,而其他都是順序執行呢?

$0:首先要明確一點,不論是串列隊列還是並行隊列,其內部任務的添加都要遵循先來後到的順序;

$1:各個隊列中任務的位置順序:主隊列中存放三個任務,順序依次為1、第一個async任務本身(以下簡稱“A1”)和5,均由主線程來運行並且A1不會等待,所以1和A1執行完畢才能執行5,這點沒問題;queue2中存放2、第二個sync任務本身(以下簡稱“A2”)、3和4四個任務,按照上述分析,dispatch添加的block任務3會在2和4之後,所以存放順序依次為2、A2、4和3,如下圖;

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我的伺服器買的是阿裡雲ECS linux系統。為了更好的操作資料庫,我希望可以用navicat for mysql管理我的資料庫。 當我按照正常的模式去鏈接mysql的時候, 報錯提示: 於是,通過查找資料,我找到瞭解決的方法,其實是阿裡雲伺服器為了安全預設不允許從外面鏈接Mysql資料庫。 下麵是 ...
  • 今天在測試MySQL的連接時候,發現連接不通過,並報錯ERROR 2003 (HY000): Can't connect to mysql server on '192.168.10.210' (111) 測試代碼: 谷歌了一下之後,原來是在mysql的my.cnf中有下麵一段代碼: 如果要讓mys ...
  • 一、概述 Oralce中的任務有2種:Job和Dbms_job,兩者的區別有: 1. jobs是oracle資料庫的對象, dbms_jobs只是jobs對象的一個實例, 就像對於tables, emp和dept都是表的實例。 2. 創建方式也有差異,Job是通過調用dbms_scheduler.c ...
  • [20190214]11g Query Result Cache RC Latches補充.txt--//上午測試鏈接:http://blog.itpub.net/267265/viewspace-2632907/--//發現自己的一個錯誤,另外寫一篇帖子更正.--//順便複習result cach ...
  • CHARINDEX作用 寫SQL語句我們經常需要判斷一個字元串中是否包含另一個字元串,但是SQL SERVER中並沒有像C#提供了Contains函數,不過SQL SERVER中提供了一個叫CHAEINDX的函數,顧名思義就是找到字元(char)的位置(index),既然能夠知道所在的位置,當然就可 ...
  • 以下幾種方法可供選擇 第一種方式: 最簡單的方法就是藉助第三方工具Navicat for MySQL來修改,方法如下: 1、登錄mysql到指定庫,如:登錄到test庫。 2、然後點擊上方“用戶”按鈕。 3、選擇要更改的用戶名,然後點擊上方的“編輯用戶”按鈕。 4、出現如圖界面,輸入新密碼,並確認新 ...
  • [20190214]11g Query Result Cache RC Latches.txt--//昨天我重覆鏈接http://www.pythian.com/blog/oracle-11g-query-result-cache-rc-latches/的測試,--//按照我的理解如果sql語句密集 ...
  • 2019-02-14 05:13:09 WARNING OGG-01519 Waiting at EOF on input trail file /home/u01/app/ogg/dirdat/MB000000002, which is not marked as complete; but su ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...