一、死鎖場景: 主線程調用主線程。
- (void)deadLockCase1 {
NSLog(@"1"); // 任務1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2"); // 任務2
});
NSLog(@"3"); // 任務3
}
控制臺輸出:1 ,后面就崩潰了。
原因:
從控制臺輸出可以看出,任務2和任務3沒有執行,此時已經死鎖了。
因為dispatch_sync是同步的,本身就會阻塞當前線程,此刻阻塞了主線程。而當前block又在等待主線程執行完畢,從而形成了主線程等待主線程,自己等自己的情況,形成了死鎖。
解決方法:
1、改用異步dispatch_async執行
NSLog(@"1"); // 任務1
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2"); // 任務2
});
NSLog(@"3"); // 任務3
控制臺輸出:1 3 2
2、不在主線程中運行,而是放在子線程中
NSLog(@"1"); // 任務1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"2"); // 任務2
});
NSLog(@"3"); // 任務3
控制臺輸出:1 2 3
注:如果block中是刷新UI的操作,則不能放在子線程中執行,會crash
二、死鎖場景2: (同步串行隊列嵌套自己)
- (void)deadLockCase2 {
dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); //任務1
dispatch_sync(aSerialDispatchQueue, ^{
NSLog(@"2"); //任務2
dispatch_sync(aSerialDispatchQueue, ^{
NSLog(@"3"); //任務3
});
NSLog(@"4"); //任務4
});
NSLog(@"5"); //任務5
}
控制臺輸出:1 2 ,執行到2后面就崩潰了。
原因:
從控制臺輸出結果來看,執行到任務2后,就已經死鎖了。因為該例子中兩個GCD都是使用的同步方式,而且還是同一個串行隊列,這就導致了和上一個例子一樣,自己在等待自己的情況,形成了死鎖。
解決方法:
1、將第二個GCD改為異步
- (void)deadLockCase2 {
dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); //任務1
dispatch_sync(aSerialDispatchQueue, ^{
NSLog(@"2"); //任務2
dispatch_async(aSerialDispatchQueue, ^{
NSLog(@"3"); //任務3
});
NSLog(@"4"); //任務4
});
NSLog(@"5"); //任務5
}
控制臺輸出:1 2 4 5 3
然而,將第一個GCD改為異步,不能解決問題
- (void)deadLockCase2 {
dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); //任務1
dispatch_async(aSerialDispatchQueue, ^{
NSLog(@"2"); //任務2
dispatch_sync(aSerialDispatchQueue, ^{
NSLog(@"3"); //任務3
});
NSLog(@"4"); //任務4
});
NSLog(@"5"); //任務5
}
控制臺輸出:
1
5
2
原因:
雖然第一個GCD是異步的,但是第二個GCD是同步的,第二個GCD在等著第一個GCD結束,而第一個GCD的block又在等著第一個GCD結束,這樣就形成了死鎖。
注:對于以上將第二個GCD改為異步,第一個GCD為同步的場景,不會造成死鎖,是因為第二個GCD為異步,它不用等待第一個GCD執行完畢,它和第一個GCD是沒有同步關系的。它是在第一個GCD執行的同時并發執行自己block的代碼。
2、將兩個GCD都改為異步
- (void)deadLockCase2 {
dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); //任務1
dispatch_async(aSerialDispatchQueue, ^{
NSLog(@"2"); //任務2
dispatch_async(aSerialDispatchQueue, ^{
NSLog(@"3"); //任務3
});
NSLog(@"4"); //任務4
});
NSLog(@"5"); //任務5
}
控制臺輸出:
1
5
2
4
3
3、使用不同的串行隊列
- (void)deadLockCase2 {
dispatch_queue_t aSerialDispatchQueue1 = dispatch_queue_create("com.test.deadlock.queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t aSerialDispatchQueue2 = dispatch_queue_create("com.test.deadlock.queue2", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); //任務1
dispatch_sync(aSerialDispatchQueue1, ^{
NSLog(@"2"); //任務2
dispatch_sync(aSerialDispatchQueue2, ^{
NSLog(@"3"); //任務3
});
NSLog(@"4"); //任務4
});
NSLog(@"5"); //任務5
}
控制臺輸出:
1
2
3
4
5
三、 死鎖場景: 信號量阻塞主線程
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore);
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
}
原因:
如果當前執行的線程是主線程,以上代碼就會出現死鎖。
因為dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了當前線程,而且等待時間是DISPATCH_TIME_FOREVER——永遠等待,這樣它就永遠的阻塞了當前線程——主線程。導致主線中的dispatch_semaphore_signal(semaphore)沒有執行,
而dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改變信號量,這樣就形成了死鎖。
解決方法:
應該將信號量移到并行隊列中,如全局調度隊列。以下場景,移到串行隊列也是可以的。但是串行隊列還是有可能死鎖的(如果執行dispatch_semaphore_signal方法還是在對應串行隊列中的話,即之前提到的串行隊列嵌套串行隊列的場景)。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore);
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
});
}
上面從上到下順序輸出
以下代碼運行結果如何?
-(void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
只輸出:1。后面就崩潰了(主線程死鎖,因為viewDidLoad方法默認開了一條主線程,然后又執行dispatch_sync(dispatch_get_main_queue(), ^{...});會導致你等我我等你,結果導致死鎖。
四、下面代碼打印順序?
//同dispatch_queue_create函數生成的concurrent Dispatch Queue隊列一起使用
dispatch_queue_t queue = dispatch_queue_create("test_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{//這里柵欄函數barrier無需等待
NSLog(@"----2----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
答案
1、2、3