不說廢話,直接上代碼
- (void)test {
dispatch_queue_t queue = dispatch_queue_create("a", NULL);
dispatch_async(queue, ^{
printf("1\n");
printf("1-%f\n", CACurrentMediaTime());
NSLog(@"1-%@", [NSThread currentThread]);
sleep(1);
dispatch_async(queue, ^{
printf("4\n");
printf("4-%f\n", CACurrentMediaTime());
NSLog(@"4-%@", [NSThread currentThread]);
sleep(1);
dispatch_async(queue, ^{
printf("6\n");
printf("6-%f\n", CACurrentMediaTime());
NSLog(@"6-%@", [NSThread currentThread]);
sleep(1);
printf("7\n");
printf("7-%f\n", CACurrentMediaTime());
});
});
});
dispatch_async(queue, ^{
printf("2\n");
printf("2-%f\n", CACurrentMediaTime());
NSLog(@"2-%@", [NSThread currentThread]);
sleep(1);
dispatch_async(queue, ^{
printf("5\n");
printf("5-%f\n", CACurrentMediaTime());
NSLog(@"5-%@", [NSThread currentThread]);
sleep(1);
});
});
dispatch_sync(queue, ^{
printf("3\n");
printf("3-%f\n", CACurrentMediaTime());
NSLog(@"3-%@", [NSThread currentThread]);
NSLog(@"3-mainThread-%@",[NSThread mainThread]);
sleep(1);
});
}
test 方法是在主線程中調用。
結論,上述代碼會依次執行 1,2,3,4,5,6,7
其中,1,2,3會卡UI,主線程卡3秒。所有任務執行總共耗時6秒。
任務3,是在主線程中執行。其余任務是在串行隊列的子線程中執行。
整個過程分析:
首先創建一個 串行隊列 隊列本身會開辟一個新線程
第一個 在主線程中 添加異步任務 添加到隊列中
第二個 在主線程中 添加異步任務 添加到隊列中
第三個 在主線程中 添加同步任務 添加到隊列中
主線程中,創建的同步任務,會導致主線程卡死,等待這第三個任務執行完畢
因為第三個任務是在線程隊列中,線程隊列中的任務按照FIFO的原則執行
第三個任務想要執行完畢,需要前兩個任務先執行完畢
串行隊列中的任務,依次執行:
先執行任務1,異步的,所以在隊列新創建的線程中執行。同時添加異步任務4到隊列中。
再執行任務2,異步的,所以在隊列新創建的線程中執行。同時添加異步任務5到隊列中。
再執行任務3,同步的,所以在 主線程 中執行。(主線程調用的同步任務)
繼續執行任務4,異步的,所以在隊列新創建的線程中執行。同時添加任務6到隊列中。
繼續執行任務5,異步的,所以在隊列新創建的線程中執行。
繼續執行任務6,異步的,所以在隊列新創建的線程中執行。
如果把 任務4 改為sync 同步的 會造成死鎖現象
因為添加任務4 的時候,是在隊列創建的那個新線程里添加的。所以隊列創建的新線程,會等待任務4完成,才可以繼續執行。但是任務4,在隊列中是第四位,他前面的任務1、2、3都還沒有執行完成。 這樣線程等待任務4完成,才能繼續執行任務1、2、3。但是任務1、2、3完不成,任務4又得不到執行。這就死鎖了。
其中比較重要的知識點:
- 串行隊列,只會創建一個新線程。無論異步任務還是同步任務,都會添加到這個隊列里,按照先進先出的原則依次執行。
- async 或者 sync,是針對調用這個任務的線程而言的。
再看一個例子,理解下為什么主線程添加dispatch_sync會死鎖
dispatch_queue_t queue = dispatch_queue_create("abc", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//do something
NSLog(@"1111");
dispatch_sync(queue, ^{
//啥也干不了
NSLog(@"2222");
});
});
先對以上代碼塊做分析
首先創建一個 串行隊列,隊列本身會開辟一個新線程
任務1 sync是在主線程中調用執行,被添加到一個新的串行隊列中。
因為串行隊列中沒有別的任務,所以任務1得到執行,打印1111,同時添加任務2 到這個新的串行隊列中。
注意,此時任務1,并沒有執行完成。因為它的代碼塊還沒有全部被執行完畢,只是執行了打印1111的那部分。
同步任務2 是被包含在任務1 的代碼塊之內,因為任務1 是在主線程中調用執行的,所以任務2 也是主線程中被調用執行。任務2 被添加到新創建的串行隊列中。
此時,主線程等待任務2,執行完成。因為任務2 與任務1 是在同一個新創建的串行隊列中,隊列中的任務以FIFO的規則執行。所以任務2 想要得到執行,任務1 需要先執行完畢才行。但由于是sync,任務1 又在等待任務2 的執行完畢。所以,會發生死鎖。
理解了上述代碼,再來看下主線程中調用dispatch_sync,添加到主隊列的過程
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3333");
});
dispatch_get_main_queue() 是系統提供的,與主線程綁定的一個默認串行隊列。
主線程中運行的所有任務都是被添加到這個隊列中。
當在viewDidLoad中,執行上述方法時,可以把上述代碼翻譯為以下代碼
dispatch_sync(dispatch_get_main_queue(), ^{
//viewDidLoad
[super viewDidLoad];
// viewDidLoad中的其他代碼
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3333");
});
});
經過上面的翻譯過程,以及前一段代碼的解釋,那么接下來,事情是不是很明朗了呢。(^_^)