一塘砸、簡(jiǎn)單問答環(huán)節(jié)
1.什么是GCD
答:一種異步執(zhí)行任務(wù)的技術(shù)
2.什么是線程
答:源代碼通過(guò)編譯器轉(zhuǎn)換為CPU命令列节仿,即二進(jìn)制代碼,一個(gè)CPU執(zhí)行這些二進(jìn)制代碼的形式為一行一行執(zhí)行掉蔬,執(zhí)行順序是線性的(永遠(yuǎn)不會(huì)出現(xiàn)分叉)廊宪,即為線程
3.什么是多線程
答:一個(gè)CPU執(zhí)行多條這種線性的無(wú)分叉路徑,即為多線程
4.一個(gè)CPU如何實(shí)現(xiàn)多線程
答:一個(gè)CPU同時(shí)只能做一件事女轿,當(dāng)CPU在多個(gè)線程間頻繁的切換箭启,由于速度很快,給人的感覺就是同時(shí)執(zhí)行多個(gè)線程
5.多線程會(huì)產(chǎn)生哪些問題
答:1.更新相同的資源谈喳,導(dǎo)致數(shù)據(jù)不一致(數(shù)據(jù)競(jìng)爭(zhēng))
2.多個(gè)線程之間相互等待,誰(shuí)也不往下執(zhí)行(死鎖)
3.多線程對(duì)內(nèi)存消耗很大
還有很多問題戈泼,不一一列舉
二婿禽、結(jié)合GCD的API講解
1.Dispatch Queue(調(diào)度隊(duì)列)
a.隊(duì)列的種類
隊(duì)列可以通俗的理解為坐火車檢票赏僧,乘客排著隊(duì)準(zhǔn)備通過(guò)檢票口,而每一個(gè)乘客相當(dāng)于一個(gè)要執(zhí)行的代碼塊(任務(wù))
隊(duì)列總體分為兩種類型 Serial Dispatch Queue 和 Concurrent Dispatch Queue扭倾,可以通俗的理解為一個(gè)檢票口淀零,和多個(gè)檢票口(即串行隊(duì)列和并行隊(duì)列)
Serial Dispatch Queue 類型的隊(duì)列每次只允許一個(gè)乘客通過(guò),后面的乘客想通過(guò)檢票口膛壹,必須要等前面的乘客先通過(guò)(每次只能執(zhí)行一個(gè)代碼塊)
Concurrent Dispatch Queue 類型的隊(duì)列相當(dāng)于有多個(gè)檢票口驾中,可以同時(shí)允許很多乘客檢票(即同時(shí)執(zhí)行很多個(gè)代碼塊)
b.創(chuàng)建一個(gè)隊(duì)列
隊(duì)列的類型是dispatch_queue_t創(chuàng)建一個(gè)隊(duì)列用dispatch_queue_create方法
// 創(chuàng)建一個(gè)Serial類型的隊(duì)列
dispatch_queue_t serial_queue = dispatch_queue_create("com.ms.serial", NULL);
// 創(chuàng)建一個(gè)concurrent類型的隊(duì)列
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.ms.concurrent", DISPATCH_QUEUE_CONCURRENT);
第一個(gè)參數(shù)是隊(duì)列的名字,一般用程序ID逆序全域名的形式模聋,這個(gè)參數(shù)在程序報(bào)錯(cuò)時(shí)候的返回信息中會(huì)體現(xiàn)肩民,還是很有用的
第二個(gè)參數(shù)為生成指定類型隊(duì)列的參數(shù)
注意
- 每生成一個(gè)Serial類型的隊(duì)列相當(dāng)于生成并使用一個(gè)線程,所以不宜生成過(guò)多的Serial類型隊(duì)列链方,這樣內(nèi)存會(huì)開銷很大
- 一個(gè)線程對(duì)數(shù)據(jù)來(lái)說(shuō)是安全的持痰,所以可以針對(duì)一個(gè)數(shù)據(jù)生成一個(gè)Serial類型的隊(duì)列,例如更新一個(gè)文件祟蚀,但是要避免針對(duì)一個(gè)文件生成多個(gè)Serial類型的隊(duì)列工窍,因?yàn)槎嗑€程的關(guān)系,會(huì)產(chǎn)生數(shù)據(jù)競(jìng)爭(zhēng)
- 當(dāng)想要對(duì)一個(gè)數(shù)據(jù)進(jìn)行并發(fā)操作前酿,又要避免數(shù)據(jù)競(jìng)爭(zhēng)問題的時(shí)候患雏,用Concurrent類型的隊(duì)列,系統(tǒng)會(huì)有效的管理這些線程罢维,避免出現(xiàn)問題
c.系統(tǒng)自帶的隊(duì)列
GCD的API為我們準(zhǔn)備了兩個(gè)隊(duì)列Main Dispatch Queue 和 Global Dispatch Queue分別對(duì)應(yīng)Serial類型的隊(duì)列和Concurrent類型的隊(duì)列
// 獲取Main Dispatch Queue
dispatch_queue_t main_queue = dispatch_get_main_queue();
// 獲取Global Dispatch Queue
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Global Dispatch Queue有四種執(zhí)行優(yōu)先級(jí)淹仑,高優(yōu)先級(jí)相當(dāng)于會(huì)員的作用,可以讓你插隊(duì)言津,對(duì)應(yīng)dispatch_get_global_queue方法的第一個(gè)參數(shù)攻人,分別是
參數(shù) | 優(yōu)先級(jí) |
---|---|
DISPATCH_QUEUE_PRIORITY_HIGH |
高優(yōu)先級(jí) |
DISPATCH_QUEUE_PRIORITY_DEFAULT |
默認(rèn)優(yōu)先級(jí) |
DISPATCH_QUEUE_PRIORITY_LOW |
低優(yōu)先級(jí) |
DISPATCH_QUEUE_PRIORITY_BACKGROUND |
后臺(tái)優(yōu)先級(jí) |
需要注意,優(yōu)先級(jí)只是大致的判斷悬槽,并不是絕對(duì)的
d.修改隊(duì)列優(yōu)先級(jí)
用dispatch_queue_create方法創(chuàng)建的隊(duì)列優(yōu)先級(jí)與Global Dispatch Queue的默認(rèn)優(yōu)先級(jí)相同怀吻,即DISPATCH_QUEUE_PRIORITY_DEFAULT
想變更生成的隊(duì)列的優(yōu)先級(jí)的時(shí)候,用方法dispatch_set_target_queue
例子是將創(chuàng)建的concurrent_queue的優(yōu)先級(jí)設(shè)置成DISPATCH_QUEUE_PRIORITY_BACKGROUND優(yōu)先級(jí)
// 創(chuàng)建的隊(duì)列初婆,優(yōu)先級(jí)是DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.ms.concurrent", DISPATCH_QUEUE_CONCURRENT);
// 獲取的Global Dispatch Queue對(duì)列蓬坡,注意優(yōu)先級(jí)是 DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 修改concurrent_queue的優(yōu)先級(jí)
dispatch_set_target_queue(concurrent_queue, global_queue);
注意
- 不可以修改Main Dispatch Queue 和 Global Dispatch Queue的優(yōu)先級(jí)
dispatch_set_target_queue的另一個(gè)作用是可以決定隊(duì)列的執(zhí)行層次,防止多個(gè)隊(duì)列并行執(zhí)行
假設(shè)有五個(gè)Serial Dispatch Queue磅叛,那么這五個(gè)Serial Dispatch Queue是并行執(zhí)行的屑咳,如果將其中四個(gè)Serial Dispatch Queue設(shè)置為其中某一個(gè)Serial Dispatch Queue的優(yōu)先級(jí),則不會(huì)出現(xiàn)并行執(zhí)行的情況
2.添加任務(wù)到隊(duì)列
前面已經(jīng)講解了如何創(chuàng)建一個(gè)隊(duì)列弊琴,下面介紹如何將任務(wù)添加到隊(duì)列中
將任務(wù)添加到隊(duì)列分為同步(sync)和異步(async)
舉個(gè)例子兆龙,你有車了,為了嘚瑟有車敲董,要開車去送你的朋友去火車站坐火車紫皇,但是你可能有好幾個(gè)朋友要去坐火車慰安,但是這幾個(gè)朋友之間相互不認(rèn)識(shí),分別在不同的地方聪铺,要分別送他們
同步(sync)就是你送一個(gè)朋友去火車站之后化焕,必須確認(rèn)你朋友經(jīng)過(guò)檢票口上了火車之后你才回去接其他的朋友,也不管其他的朋友是否著急
異步(async)就是你送去一個(gè)朋友之后铃剔,也沒管它是否檢票上車撒桨,就返回去接其他的朋友了
a. dispatch_sync()
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
將任務(wù)同步添加到隊(duì)列中
例子是將打印1的任務(wù)同步添加到serial_queue隊(duì)列中
dispatch_queue_t serial_queue = dispatch_queue_create("com.ms.serial", NULL);
dispatch_sync(serial_queue, ^{
// 要做的任務(wù)
NSLog(@"1");
});
- 第一個(gè)參數(shù)是要添加的隊(duì)列
- 第二個(gè)參數(shù)是block,就是我們要添加的任務(wù)
b. dispatch_sync_f()
void dispatch_sync_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
作用和dispatch_sync()方法一樣键兜,不同點(diǎn)是這個(gè)方法可以將函數(shù)提交到隊(duì)列中
例子中是將一個(gè)函數(shù)print1()添加到隊(duì)列中凤类,實(shí)現(xiàn)的也是打印1的任務(wù)
void print1(void *param){
printf("%s", param);
}
char *str = "1";
dispatch_sync_f(serial_queue, str, print1);
- 第一個(gè)參數(shù)是要添加的隊(duì)列
- 第二個(gè)參數(shù)是地址,這個(gè)參數(shù)是第三個(gè)參數(shù)(一個(gè)函數(shù))要用的參數(shù)
- 第三個(gè)參數(shù)是一個(gè)
void (*dispatch_function_t)(void *_Nullable)
類型的函數(shù)
c. dispatch_async()和dispatch_async_f()
類比上面的a和b蝶押,就是同步和異步的區(qū)別
將任務(wù)添加到隊(duì)列后踱蠢,系統(tǒng)會(huì)對(duì)隊(duì)列進(jìn)行retain,當(dāng)任務(wù)完成后隊(duì)列被release
注意
- dispatch_sync()和dispatch_async()中第二個(gè)參數(shù)是block棋电,這個(gè)block中即使使用self也不會(huì)出現(xiàn)循環(huán)引用的情況茎截,可以放心使用
- dispatch_sync()別亂用,在串行隊(duì)列中用dispatch_sync()將任務(wù)添加到當(dāng)前串行隊(duì)列赶盔,會(huì)發(fā)生死鎖企锌,因?yàn)閎lock的執(zhí)行需要等待前面的任務(wù),也就是dispatch_sync執(zhí)行完成于未,兩者互相等待撕攒,在并行隊(duì)列中就不會(huì)出現(xiàn)這種情況
- 前面介紹過(guò)Main Dispatch Queue是串行隊(duì)列,你懂的??