把任務添加到隊列,而且指定執行任務的函數面試
dispatch_async{}
dispatch_sync{}
// 把任務添加到隊列 --> 函數
// 任務
dispatch_block_t block = ^{
NSLog(@"hello GCD");
};
//串行隊列
dispatch_queue_t queue = dispatch_queue_create("com.zb.cn", NULL);
// 函數
dispatch_async(queue, block);
複製代碼
隊列: 數組
特殊的兩種隊列:安全
主隊列 dispatch_get_main_queue()
bash
全局隊列 dispatch_get_global_queue()
多線程
隊列與函數:併發
理解上面幾種組合後,嘗試解答出下面的任務輸出順序、、異步
問題一
- (void)textOne {
dispatch_queue_t queue = dispatch_queue_create("zb", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"任務1");
dispatch_async(queue, ^{
NSLog(@"任務2");
dispatch_async(queue, ^{
NSLog(@"任務3");
});
NSLog(@"任務4");
});
NSLog(@"任務5");
}
複製代碼
首先明確是一個併發隊列,裏面有任務1
、dispatch_async
block任務、任務5
,因此按順序輸出任務1
、任務5
;裏面嵌套的異步操做和外面的分析如出一轍,即整個的輸出順序爲一、五、二、四、3async
問題二
- (void)textTwo {
dispatch_queue_t queue = dispatch_queue_create("zb", DISPATCH_QUEUE_SERIAL);
NSLog(@"任務1");
dispatch_async(queue, ^{
NSLog(@"任務2");
dispatch_sync(queue, ^{
NSLog(@"任務3");
});
NSLog(@"任務4");
});
NSLog(@"任務5");
}
複製代碼
這裏是一個串行隊列,dispatch_async
任務開啓了一個線程專門處理,沒必要等待,因此先按順序輸出任務1
、任務5
;進入第一個dispatch_async
任務,串行隊列,因此也是按順序執行任務2
、dispatch_async
block任務、任務4
;此時的block任務是一個同步函數,因此當任務2
執行完畢之後,走到這個發現是同步,而後就把任務3
加入到隊列裏執行,此時隊列裏的任務是任務2
、dispatch_async
block任務、任務4
、任務3
;根據 FIFO
原則正常行走,任務2
結束後,執行dispatch_async
block任務,可是由於同步的緣由,執行這個block任務又必需要執行任務3
,執行任務3
的前提是任務4
執行結束,執行任務4
的前提是block任務執行結束,這裏發生裏死鎖。因此任務的輸出順序爲任務1
、任務5
、任務2
,而後奔潰。函數
死鎖的產生url
若是把上面的串行隊列改爲併發隊列,輸出的結果又是什麼樣的呢?
下面看一個面試題
- (void)viewDidLoad {
[super viewDidLoad];
int a = 0;
while (a < 10) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
a ++;
});
}
NSLog(@"%d",a);
}
複製代碼
問題一:a++ 報錯的緣由?
問題二:修改正確後,輸出a=?
問題三:在不改變函數以及隊列的前提下,如何讓a的輸出爲10?
答案一:
__block
修飾a
的初始化,把a
的指針和值從棧區copy
到struct
,堆區。
答案二:輸出結果a >= 10
,在while
循環裏,每一次的循環都會產生一個線程,執行異步操做,不等待直接執行後面的任務,同時這也是耗時操做,因此在循環裏可能會走不少次a++
操做
答案三:能夠經過加鎖的方式,實現輸出a=10
具體代碼以下
- (void)viewDidLoad {
[super viewDidLoad];
__block int a = 0;
// 信號量
dispatch_semaphore_t lock = dispatch_semaphore_create(1);
while (a < 10) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
a ++;
dispatch_semaphore_signal(lock);
});
// 堵死
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
}
NSLog(@"%d",a);
}
複製代碼
dispatch_barrier_sync
- (void)demo2{
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
/* 1.異步函數 */
dispatch_async(concurrentQueue, ^{
for (NSUInteger i = 0; i < 5; i++) {
NSLog(@"download1-%zd-%@",i,[NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (NSUInteger i = 0; i < 5; i++) {
NSLog(@"download2-%zd-%@",i,[NSThread currentThread]);
}
});
/* 2. 柵欄函數 */
dispatch_barrier_sync(concurrentQueue, ^{
NSLog(@"---------------------%@------------------------",[NSThread currentThread]);
});
NSLog(@"加載那麼多,喘口氣!!!");
/* 3. 異步函數 */
dispatch_async(concurrentQueue, ^{
for (NSUInteger i = 0; i < 5; i++) {
NSLog(@"平常處理3-%zd-%@",i,[NSThread currentThread]);
}
});
NSLog(@"**********起來幹!!");
dispatch_async(concurrentQueue, ^{
for (NSUInteger i = 0; i < 5; i++) {
NSLog(@"平常處理4-%zd-%@",i,[NSThread currentThread]);
}
});
}
複製代碼
這裏就達到了download1
、download2
任務完成後,纔去執行平常處理3
、平常處理4
任務的效果。
提問1:若是把併發隊列
dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
改爲dispatch_get_global_queue(0,0);
後效果會怎麼樣?
執行代碼後會神奇的發現,柵欄效果神奇的失效了。
這裏須要注意了,柵欄函數必定要是自定義的併發隊列,否則就無效,分析一下也能夠得知,dispatch_get_global_queue
是全局的併發隊列,加上柵欄實際上就是一個堵塞,若是有效的話,系統就。。GG了。
提問2:若是把
download1
或download2
任務的隊列換成一個其餘的隊列,效果會怎麼樣?
執行代碼後,也會發現,不在同一個隊列的話,柵欄也是無效,因此這裏也是一個須要注意的地方,必需要求都在同一個隊列。
這是柵欄函數的第一個做用,保證順序執行。
看下面代碼:
for (int i = 0; i < 5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
}
複製代碼
執行後,會發生crash,異步函數,建立了多條線程,同時對數組執行addObject
操做,形成資源搶奪,發送崩潰。
dispatch_queue_t concurrentQueue = dispatch_queue_create("zb", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 5000; i++) {
dispatch_async(concurrentQueue, ^{
.
.
.
dispatch_barrier_async(concurrentQueue, ^{
[self.mArray addObject:image];
});
});
}
複製代碼
執行後,能夠正常執行,輸出結果。
第二個做用,保證線程安全。
group
建立組 dispatch_group_create
進組任務 dispatch_group_async
進組任務執行完畢通知: dispatch_group_notify
進組任務執行等待時間:dispatch_group_wait
或
進組 dispatch_group_enter
出組 dispatch_group_leave
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"任務1");
});
long timeOut = dispatch_group_wait(group, 0.5);
dispatch_group_notify(group, queue, ^{
NSLog(@"任務2");
});
或
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"任務");
dispatch_group_leave(group);
});
複製代碼
使用enter
與leave
時,必定要成對出現,否則會產生crash。
dispatch_semaphore_t
建立信號量 dispatch_semaphore_create
信號量等待 dispatch_semaphore_wait
信號量釋放 dispatch_semaphore_signal
能夠看成鎖來使用,在本文一開始就使用了,還能夠控制GCD最大併發數dispatch_semaphore_create(x)