前言
其實在我寫這邊博客之前,也在查閱不好資料,但是發現網上很多人說的內容總結,其實并不正確,導致自己也踩了不少坑,所以才想著重新總結一下,給自己做個參考,也當是復習一下,當然我也可能有不對的地方,希望可以得到改正
知識點梳理
基本概念簡述
-
1.同步函式dispatch_sync 必須等待當前陳述句執行完畢,才會進行下一條,在當前執行block任務
-
2.異步函式dispatch_async 不用等當前陳述句執行完畢,就可以執行下一條陳述句,會開啟執行緒執行block,異步多執行緒的代名詞,主佇列例外
還原最基礎的寫法,很重要
- (void)syncTest{
// 把任務添加到佇列 --> 函式
// 任務 _t ref c物件
dispatch_block_t block = ^{
NSLog(@"hello GCD");
};
//串行佇列
dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", NULL);
// 函式
dispatch_async(queue, block);
}
上面就是把同步函式步驟進行拆分,也是為了便于理解
而接下來會以實際例子去測驗再結合理論知識去總結概括,為了方便大家查閱,我全部是都代碼形式復制過來,沒有截圖,也方便大家更快的找到自己想看的題目型別,當然也希望有大佬給出意見和新的題型讓我補充🙏🙏🙏🙏
建議大家先分析之后在看結果,我驗證的時候翻車不少😂😂😂😂
例題1:
/**
主佇列同步
不會開執行緒
*/
- (void)mainSyncTest{
NSLog(@"0");
// 等
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1");
});
NSLog(@"2");
}
決議:主佇列 + 同步函式(sync函式) 產生死鎖 1 2互相等待
例題2:
/**
串行同步佇列 : FIFO: 先進先出
*/
- (void)serialSyncTest{
//1:創建串行佇列
dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i<20; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
}
決議:串行佇列 先進先出 雖然連續添加了20次任務 但是必須依次執行,所以結果是依次列印1-19
例題3:
/**
主佇列異步
不會開執行緒 順序
*/
- (void)mainAsyncTest{
NSLog(@"0");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"1---%@",[NSThread currentThread]);
});
NSLog(@"2---%@",[NSThread currentThread]);
}
決議:主佇列 串行 + 異步 還是依次執行 但是 1 必須等 2執行
例題4:
/**
全域異步
全域佇列:一個并發佇列
*/
- (void)globalAsyncTest{
for (int i = 0; i<20; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
決議:全域佇列 并發 + 異步 多執行緒同步執行 執行次數還是20次 只不過不同執行緒執行次數隨機
例題5:
/**
異步并發: 有了異步函式不一定開辟執行緒
*/
- (void)concurrentAsyncTest{
//1:創建并發佇列
dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<20; i++) {
dispatch_async(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
決議:結果和上面差不多 會另開執行緒,但是開幾條執行緒不定,看執行緒池調度狀況
例題6:
/**
全域同步
全域佇列:一個并發佇列
*/
- (void)globalSyncTest{
for (int i = 0; i<20; i++) {
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
決議:全域佇列 并發 + 同步 依次執行 NSLog 最后執行,不開辟新執行緒
例題7:
/**
同步并發 : 堵塞 同步鎖 佇列 : resume supend 執行緒 操作, 佇列掛起 任務能否執行
*/
- (void)concurrentSyncTest{
//1:創建并發佇列
dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<20; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
決議:上面全域佇列結果一樣
例題8:
- (void)textDemo1{
dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
決議:并發佇列 異步+同步 結果:1 5和(234)并發 也就是說234順序不會變 而5可能在他們之間任意位置出現 大概率15234
例題9:
- (void)textDemo{
dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
// 耗時
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
決議:并發佇列 異步+異步 結果:1 首位 2先于34,34位置不定 而5可在234任意位置 大概率是15243 一般情況下主執行緒肯定執行更穩定 另開執行緒在執行任務會稍微慢些 雖然是并發,但是本質CPU快速切換的程序 所以才說大概率是15243
例題10:
/**
串行異步佇列
*/
- (void)serialAsyncTest{
//1:創建串行佇列
dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i<20; i++) {
dispatch_async(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
決議:其實和第一個例子相似,主佇列也是串行佇列,所以串行佇列異步,不開執行緒,并且異步函式要在當前執行緒函式任務執行完畢在執行,所以NSLog永遠先執行
例題11:
/**
可變陣列 執行緒不安全 解決辦法
*/
- (void)demo3{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
//答案不確定
//dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); 答案1000
for (int i = 0; i<1000; 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];
dispatch_barrier_async(concurrentQueue , ^{
[self.mArray addObject:image];
});
});
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"陣列的個數:%zd",self.mArray.count);
}
決議:柵欄函式 配合并發佇列可以對異步并發起到攔截作用,確保在陣列插入資料程序的執行緒安全,依次插入資料,但是柵欄函式只能對自定義佇列有效,系統佇列不行,因此答案不確定,如果是自定義并發佇列,就是陣列個數就是1000
同時這里也體現了柵欄函式使用的局限性,它只能對同一個佇列進行攔截操作,如果是不同佇列就行不通了,比如在我們使用一些第三方庫的方法時,你也不知道他們底層是使用的什么佇列,自然而然沒辦法使用柵欄函式處理
例題12:
- (void)demo2{
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t concurrentQueue1 = dispatch_queue_create("kc", DISPATCH_QUEUE_CONCURRENT);
// dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
// 這里是可以的額!
/* 1.異步函式 */
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
sleep(1);
NSLog(@"2");
});
/* 2. 柵欄函式 */ // - dispatch_barrier_sync
dispatch_barrier_sync(concurrentQueue, ^{
NSLog(@"----%@-----",[NSThread currentThread]);
});
/* 3. 異步函式 */
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
// 4
NSLog(@"4");
}
決議:柵欄函式同步+并發佇列 依然可以起到同步的作用,但是會阻塞當前執行緒,也就是說3 要等 1和2執行完畢,且柵欄函式的任務也執行完畢 ,才能執行,同時4也必須等柵欄函式執行完畢,所以12隨機 先于 34隨機
例題13:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
sleep(2);
NSLog(@"執行任務1");
NSLog(@"任務1完成");
});
//任務2
dispatch_async(queue, ^{
sleep(2);
NSLog(@"執行任務2");
NSLog(@"任務2完成");
dispatch_semaphore_signal(sem); // 發信號
});
//任務3
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"執行任務3");
NSLog(@"任務3完成");
dispatch_semaphore_signal(sem);
});
決議:初始信號量0,全域并發佇列異步函式,按理說并發執行,但是初始信號量0,所以任務1和任務3都在等待,任務2執行完畢后,發送信號,先等待的先接受,所以大概率執行任務1,任務3不執行
例題14:
dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
//任務1
dispatch_async(queue, ^{
sleep(10);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
NSLog(@"執行任務1");
NSLog(@"任務1完成");
});
//任務2
dispatch_async(queue, ^{
NSLog(@"執行任務2");
NSLog(@"任務2完成");
dispatch_semaphore_signal(sem); // 發信號
});
//任務3
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"執行任務3");
NSLog(@"任務3完成");
dispatch_semaphore_signal(sem);
});
//任務4
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"執行任務4");
NSLog(@"任務4完成");
dispatch_semaphore_signal(sem);
});
決議:首先任務2先執行毫無疑問,任務3和4會先于1,因為1卡執行緒10秒,等待會比3和4晚,而3和4執行后會在發送一下信號,而任務1呢,因為信號的機制,盡管等待10秒,但只要執行等待信號函式,就能接收到信號量現在還是1,所以還是會執行
例題15:
dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue1 = dispatch_queue_create("ycx", NULL);
//任務1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // 等待
sleep(2);
NSLog(@"執行任務1");
NSLog(@"任務1完成");
});
//任務2
dispatch_async(queue1, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"執行任務2");
NSLog(@"任務2完成");
});
//任務3
dispatch_async(queue1, ^{
sleep(2);
NSLog(@"執行任務3");
NSLog(@"任務3完成");
dispatch_semaphore_signal(sem);
});
//任務4
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"執行任務4");
NSLog(@"任務4完成");
dispatch_semaphore_signal(sem);
});
決議:首先要看清楚queue和queue1兩個自定義佇列,一個并發,一個串行,因為任務2和3是在串行佇列執行,盡管是異步函式,但是沒有另開執行緒,而任務2又有信號等待造成執行緒阻塞,無法執行完畢,那么任務3也就沒辦法執行,也就沒發發送信號,所以沒有任務能夠執行
例題16:
實作一個場景:圖片異步加載,確保全部加載完成后,再使用,保證資料安全
第一個方案:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self) weakSelf = self;
dispatch_group_async(group, queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
UIImage *image1 = [UIImage imageWithData:data1];
dispatch_barrier_async(queue, ^{
[strongSelf.mArray addObject:image1];
NSLog(@"showTime----111111111");
});
});
dispatch_group_async(group, queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
UIImage *image2 = [UIImage imageWithData:data2];
dispatch_barrier_async(queue, ^{
[strongSelf.mArray addObject:image2];
NSLog(@"showTime----222222222");
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"陣列個數:%ld",self.mArray.count);
});
決議:常見調度組的使用場景,確保圖片異步加載完畢,再統一處理,使用柵欄函式確保陣列寫入安全
第二個方案:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue1 = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self) weakSelf = self;
dispatch_group_async(group, queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
UIImage *image1 = [UIImage imageWithData:data1];
dispatch_barrier_sync(queue1, ^{
[strongSelf.mArray addObject:image1];
NSLog(@"showTime----111111111");
});
});
dispatch_group_async(group, queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
UIImage *image2 = [UIImage imageWithData:data2];
dispatch_barrier_sync(queue1, ^{
[strongSelf.mArray addObject:image2];
NSLog(@"showTime----222222222");
});
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"陣列個數:%ld",self.mArray.count);
決議:dispatch_group_wait函式會等待前面group里面的任務執行完畢在執行,后面的任務,會阻塞當前執行緒,從而達到統一處理,同時使用dispatch_barrier_sync和并發佇列,確保資料寫入的安全,切記使用dispatch_barrier_sync是一定要另外再創建一個佇列,如果和調度組使用同一個佇列,就會造成死鎖,互相等待執行,如下
例題17:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self) weakSelf = self;
dispatch_group_async(group, queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
UIImage *image1 = [UIImage imageWithData:data1];
dispatch_barrier_sync(queue, ^{
[strongSelf.mArray addObject:image1];
NSLog(@"showTime----111111111");
});
NSLog(@"showTime----333333");
});
dispatch_group_async(group, queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
UIImage *image2 = [UIImage imageWithData:data2];
dispatch_barrier_sync(queue, ^{
[strongSelf.mArray addObject:image2];
NSLog(@"showTime----222222222");
});
NSLog(@"showTime----444444");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"陣列個數:%ld",self.mArray.count);
決議:死鎖在于 dispatch_barrier_sync 會堵塞當前執行緒執行,而柵欄函式又必須等待前面加入佇列queue的任務執行完畢才可以執行,最終就會在queue佇列中的某個執行緒死鎖,group調度組任務永遠無法執行完畢,dispatch_group_wait就一直等待
例題18:
接著例題16的第三個方案
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self) weakSelf = self;
dispatch_group_enter(group);
dispatch_async(queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
UIImage *image1 = [UIImage imageWithData:data1];
dispatch_barrier_async(queue, ^{
[strongSelf.mArray addObject:image1];
NSLog(@"showTime----111111111");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//創建調度組
NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
UIImage *image2 = [UIImage imageWithData:data2];
dispatch_barrier_async(queue, ^{
[strongSelf.mArray addObject:image2];
NSLog(@"showTime----222222222");
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"陣列個數:%ld",self.mArray.count);
});
決議:dispatch_group_enter和dispatch_group_leave 進組出組配合使用,有點像信號量,也是必須進出組剛好對等,才可以執行dispatch_group_notify里面的任務
如果后續發現一些有意思的題目,也會繼續收錄補充,主要也是為了加深對于多執行緒的理解和運用
今天的分享就到次為止,下一篇會著重分析GCD的底層原始碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/295686.html
標籤:其他
下一篇:vue頁面跳轉取消上一個頁面請求
