1. 程式人生 > >GCD問題:佇列 + 任務 是否等於 死鎖

GCD問題:佇列 + 任務 是否等於 死鎖

一:GCD 執行緒保活功能

  • GCD 沒有執行緒保活功能。
  • 執行緒保活功能 只能通過 runloop 來執行。
  • GCD 只是在block 中執行程式碼,在block 中開啟runloop。
  • GCD 和 runloop 不是同一回事。
  • GCD 只負責開啟執行緒,然後去執行任務。不負責執行緒保活。
  • 就算 GCD 的執行緒活下來了,也是因為 GCD裡面用了 runloop。

使用sync 函式 往當前 序列佇列中新增任務,會卡住當前的序列佇列(產生死鎖)

二:主佇列 + 同步任務 = 死鎖

- (void)viewDidLoad {
    [super viewDidLoad];
    
     NSLog(@"執行任務1");
    [self GCDDeadlock];
     NSLog(@"執行任務3");
}

- (void)GCDDeadlock {
    // 問題: 以下程式碼是在主執行緒執行的,會不會產生死鎖?
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"執行任務2");
    })
}

執行結果:
在這裡插入圖片描述

  • 崩潰資訊: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  • 主佇列 + 同步任務 = 死鎖

第一種解釋:

  • dispatch_get_main_queue() : 新增到主佇列,代表要新增到主執行緒執行任務
  • dispatch_sync 的block中的程式碼是要新增到主佇列,意味著: 要新增到主執行緒中去執行
  • 而dispatch_sync是同步任務, 意味著:要在當前執行緒去執行。
  • 在當前執行緒中執行,又是主佇列。那就意味著,block 中的內容要放在主執行緒中執行
  • 可能會認為 這不就直接 1, 2, 3 的執行了麼? 為什麼會死鎖。
  • 因為 queue 是佇列,佇列的一個基本含義就是:排隊,FIFO(先進先出First In First Out). 哪個任務先進來,就先執行哪個任務。
  • 首先,將 block 中的任務 放到 main_queue(主佇列)中,將來主佇列 需要取出裡面的任務來執行。
  • 但實際上主執行緒已經有ViewDidLoad 這個任務了。 ViewDidLoad 方法的執行就是一個完整的任務
  • 如果想要從 queue 中取出 block 中的任務的話,需要等 上一個(ViewDidLoad)任務執行完畢,才能執行這個任務。
  • 由於 block 任務是 dispatch_sync (馬上在當前執行緒中執行任務),也就意味著,我希望馬上要執行完block 裡面的東西。執行完 block 中的內容後,才能執行下邊的任務(NSLog(@“執行任務3”);這句程式碼)。

結論:

  • 要想執行 block中的內容,需要 先把 ”執行任務3“ 執行完畢,但是要想執行 ”執行任務3“ 就得把 dispatch_sync 執行完畢。
  • 任務2 等任務3 執行完畢;
  • 任務3 等任務2 執行完畢;
  • 最終導致死鎖。

第二種解釋:

  • queue 是主佇列,會在主執行緒執行任務
  • dispatch_sync 是同步任務: 在當前執行緒中執行任務,不具備開啟新執行緒的能力
  • 主佇列 + 同步任務:沒有開啟新執行緒,序列執行任務
  • 序列執行任務:一個任務執行完畢後,再執行下一個任務。
  • ViewDidLoad 方法 是一個任務
  • dispatch_sync 的 block 也是一個任務
  • 從上到下執行任務,按理說應該是 “任務1”, “任務2” ,“任務3”。 但是 dispatch_sync 是同步扔一個 block 到 queue 中,扔了 我就等著。等到 queue 排隊把這個block 執行完了之後,才繼續執行下一行程式碼。
  • 比如 viewDidLoad 在書執行緒 ,排隊為 A;
  • dispatch_sync 也在主執行緒,排隊為B;
  • A 在執行一半的時候,需要等到自己的下一個任務B 執行完,自己才能繼續執行。
  • 而 B 排隊在後面,需要等A 執行完畢才能執行。
  • 這時死鎖就產生了。 A 和 B 互相等待執行,當前執行緒就卡死在 dispatch_sync 這行程式碼處。

三、主佇列 + 非同步任務 = 不死鎖

- (void)viewDidLoad {
    [super viewDidLoad];
    
     NSLog(@"執行任務1");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"執行任務2");
    });

     NSLog(@"執行任務3");
}

列印結果:
在這裡插入圖片描述

  • dispatch_async 不要求立馬在當前執行緒同步執行任務

四、 會不會產生死鎖

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"執行任務1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"執行任務2");
        
        dispatch_sync(queue, ^{
            NSLog(@"執行任務3");
        });
        NSLog(@"執行任務4");
    });
    NSLog(@"執行任務5");
}

執行結果:會產生死鎖

  • 在 dispatch_sync 這行會產生死鎖
    在這裡插入圖片描述

五、兩個不一樣的佇列,會不會產生死鎖

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"執行任務1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
   // 建立併發佇列
    dispatch_queue_t queue1 = dispatch_queue_create("WYqueue1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"執行任務2");
        
        dispatch_sync(queue1, ^{
            NSLog(@"執行任務3");
        });
        NSLog(@"執行任務4");
    });
    NSLog(@"執行任務5");
}

不會產生死鎖。
執行順序是:
在這裡插入圖片描述

六、兩個不同的序列佇列,會不會死鎖

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"執行任務1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue1 = dispatch_queue_create("WYqueue1", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"執行任務2");
        
        dispatch_sync(queue1, ^{
            NSLog(@"執行任務3");
        });
        NSLog(@"執行任務4");
    });
    NSLog(@"執行任務5");
}

不會死鎖。
執行順序
在這裡插入圖片描述

七、兩個任務用一個併發佇列

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"執行任務1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"執行任務2");
        
        dispatch_sync(queue, ^{
            NSLog(@"執行任務3");
        });
        NSLog(@"執行任務4");
    });
    NSLog(@"執行任務5");
}

不會產生死鎖
併發佇列:可以執行多個任務。
執行結果
在這裡插入圖片描述

八、全域性佇列 、併發佇列
在這裡插入圖片描述

  • 全域性佇列:不管建立幾次,地址都一樣;
  • 併發佇列:不管建立幾次,地址都不一樣。

列印地址結果:
在這裡插入圖片描述

參考:
如何安全使用dispatch_sync