1.什么是程序, 進(jìn)程, 線程
程序: 由源代碼生成的可執(zhí)行應(yīng)用
進(jìn)程: 一個(gè)正在運(yùn)行的程序可以看做一個(gè)進(jìn)程, 進(jìn)程擁有獨(dú)立運(yùn)行所需的全部資源
進(jìn)程的作用:負(fù)責(zé)給應(yīng)用程序分配內(nèi)存空間(該空間是受保護(hù)的,獨(dú)立的)
線程: 程序中獨(dú)立運(yùn)行的代碼段
線程的作用:負(fù)責(zé)執(zhí)行應(yīng)用程序中的代碼按厘,在系統(tǒng)中運(yùn)行著的程序的代碼只能由線程執(zhí)行
線程創(chuàng)建過(guò)程:應(yīng)用程序在啟動(dòng)過(guò)程中,系統(tǒng)會(huì)自動(dòng)創(chuàng)建默認(rèn)的線程,也就是程序的主線程/UI線程
線程與進(jìn)程之間的關(guān)系:一個(gè)進(jìn)程至少有一個(gè)線程(即主線程)挖腰,一個(gè)進(jìn)程中可以有多個(gè)線程
子線程在執(zhí)行完自己的任務(wù)后會(huì)自動(dòng)銷毀
2.開(kāi)發(fā)原則
1痴怨、比較耗時(shí)的操作都放到子線程中(一般是在進(jìn)行網(wǎng)絡(luò)請(qǐng)求的時(shí)候文判,或者是執(zhí)行時(shí)間不可控的時(shí)候)
2、UI操作橡庞、與用戶交互的代碼都放到主線程中,其一是因?yàn)闉榱吮WC用戶操作的流程性印蔗,其二是因?yàn)樗械腢I控件都在UIKIT框架中扒最,而UIKIT框架采用的就是這種機(jī)制,蘋果公司也推薦這種用法华嘹,都是非線程安全的吧趣,為了保證正確,將所有的用戶交互的代碼放到主線程中耙厚,而單一的線程是按順序執(zhí)行的强挫,所以就避免了非線程安全的問(wèn)題
3.線程內(nèi)的代碼的執(zhí)行順序:
串行執(zhí)行:同一線程中,該線程中的任務(wù)代碼按順序執(zhí)行薛躬,同一時(shí)間只能執(zhí)行一塊代碼
并發(fā)執(zhí)行:一個(gè)進(jìn)程可以創(chuàng)建多個(gè)子線程俯渤,在不同的線程中,任務(wù)同時(shí)執(zhí)行(其實(shí)是一種假象泛豪,是因?yàn)镃PU的調(diào)度速度非吵砘澹快速,所以感覺(jué)是一起執(zhí)行的)(即多線程)
并行執(zhí)行:真正的同時(shí)執(zhí)行诡曙,由多個(gè)CPU同時(shí)執(zhí)行
4.多線程的優(yōu)點(diǎn)
a臀叙、能適當(dāng)提高程序的執(zhí)行效率
b、能適當(dāng)提高資源利用率(CPU价卤、內(nèi)存利用率)
5.多線程的缺點(diǎn)
a劝萤、開(kāi)啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M慎璧,子線程占用512KB)床嫌,如果開(kāi)啟大量的線程,會(huì)占用大量的內(nèi)存空間胸私,降低程序的性能
b厌处、線程越多,CPU在調(diào)度線程上的開(kāi)銷就越大
c'程序設(shè)計(jì)更加復(fù)雜:比如線程之間的通信岁疼、多線程的數(shù)據(jù)共享
6.線程安全問(wèn)題
線程安全:保證多條線程進(jìn)行讀寫操作,都能夠得到正確的結(jié)果!
使用線程同步技術(shù)
解決方案:互斥鎖
優(yōu)點(diǎn):能有效防止因多線程搶奪資源而引起的數(shù)據(jù)安全問(wèn)題!
缺點(diǎn):需要消耗大量的CPU資源!
原理:多條線程在同一條線上按順序執(zhí)行任務(wù)!
GCD
Grand Central Dispatch阔涉,聽(tīng)名字就霸氣。它是蘋果為多核的并行運(yùn)算提出的解決方案,所以會(huì)自動(dòng)合理地利用更多的CPU內(nèi)核(比如雙核瑰排、四核)贯要,最重要的是它會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)椭住、銷毀線程)崇渗,完全不需要我們管理,我們只需要告訴干什么就行京郑。同時(shí)它使用的也是c語(yǔ)言宅广,不過(guò)由于使用了 Block(Swift里叫做閉包),使得使用起來(lái)更加方便傻挂,而且靈活乘碑。所以基本上大家都使用GCD這套方案,老少咸宜金拒,實(shí)在是居家旅行兽肤、殺人滅口,必備良藥绪抛。不好意思资铡,有點(diǎn)中二,咱們繼續(xù)幢码。
任務(wù)和隊(duì)列
在GCD中笤休,加入了兩個(gè)非常重要的概念:任務(wù)和隊(duì)列。
任務(wù):即操作症副,你想要干什么店雅,說(shuō)白了就是一段代碼,在 GCD 中就是一個(gè) Block贞铣,所以添加任務(wù)十分方便闹啦。任務(wù)有兩種執(zhí)行方式:同步執(zhí)行和異步執(zhí)行,他們之間的區(qū)別是是否會(huì)創(chuàng)建新的線程辕坝。
這里說(shuō)的并不準(zhǔn)確窍奋,同步(sync)和異步(async)的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程,直到Block中的任務(wù)執(zhí)行完畢酱畅!
如果是同步(sync)操作琳袄,它會(huì)阻塞當(dāng)前線程并等待Block中的任務(wù)執(zhí)行完畢,然后當(dāng)前線程才會(huì)繼續(xù)往下運(yùn)行纺酸。
如果是異步(async)操作窖逗,當(dāng)前線程會(huì)直接往下執(zhí)行,它不會(huì)阻塞當(dāng)前線程餐蔬。
隊(duì)列:用于存放任務(wù)滑负。一共有兩種隊(duì)列在张,串行隊(duì)列和并行隊(duì)列。
串行隊(duì)列中的任務(wù)會(huì)根據(jù)隊(duì)列的定義 FIFO 的執(zhí)行矮慕,一個(gè)接一個(gè)的先進(jìn)先出的進(jìn)行執(zhí)行。
更新:放到串行隊(duì)列的任務(wù)啄骇,GCD 會(huì)FIFO(先進(jìn)先出)地取出來(lái)一個(gè)痴鳄,執(zhí)行一個(gè),然后取下一個(gè)缸夹,這樣一個(gè)一個(gè)的執(zhí)行痪寻。
并行隊(duì)列中的任務(wù)根據(jù)同步或異步有不同的執(zhí)行方式。
更新:放到并行隊(duì)列的任務(wù)虽惭,GCD 也會(huì)FIFO的取出來(lái)橡类,但不同的是,它取出來(lái)一個(gè)就會(huì)放到別的線程芽唇,然后再取出來(lái)一個(gè)又放到另一個(gè)的線程顾画。這樣由于取的動(dòng)作很快,忽略不計(jì)匆笤,看起來(lái)研侣,所有的任務(wù)都是一起執(zhí)行的。不過(guò)需要注意炮捧,GCD 會(huì)根據(jù)系統(tǒng)資源控制并行的數(shù)量庶诡,所以如果任務(wù)很多,它并不會(huì)讓所有任務(wù)同時(shí)執(zhí)行咆课。
雖然很繞末誓,但請(qǐng)看下表:
同步執(zhí)行異步執(zhí)行
串行隊(duì)列當(dāng)前線程,一個(gè)一個(gè)執(zhí)行其他線程书蚪,一個(gè)一個(gè)執(zhí)行
并行隊(duì)列當(dāng)前線程喇澡,一個(gè)一個(gè)執(zhí)行開(kāi)很多線程,一起執(zhí)行
創(chuàng)建隊(duì)列
主隊(duì)列:這是一個(gè)特殊的串行隊(duì)列善炫。什么是主隊(duì)列撩幽,大家都知道吧,它用于刷新 UI箩艺,任何需要刷新 UI 的工作都要在主隊(duì)列執(zhí)行窜醉,所以一般耗時(shí)的任務(wù)都要放到別的線程執(zhí)行。
//OBJECTIVE-Cdispatch_queue_tqueue= ispatch_get_main_queue();//SWIFTletqueue= ispatch_get_main_queue()
自己創(chuàng)建的隊(duì)列:凡是自己創(chuàng)建的隊(duì)列都是串行隊(duì)列艺谆。其中第一個(gè)參數(shù)是標(biāo)識(shí)符榨惰,用于 DEBUG 的時(shí)候標(biāo)識(shí)唯一的隊(duì)列,可以為空静汤。大家可以看xcode的文檔查看參數(shù)意義琅催。
更新:自己可以創(chuàng)建串行隊(duì)列, 也可以創(chuàng)建并行隊(duì)列居凶。看下面的代碼(代碼已更新)藤抡,它有兩個(gè)參數(shù)侠碧,第一個(gè)上面已經(jīng)說(shuō)了,第二個(gè)才是最重要的缠黍。
第二個(gè)參數(shù)用來(lái)表示創(chuàng)建的隊(duì)列是串行的還是并行的弄兜,傳入DISPATCH_QUEUE_SERIAL或NULL表示創(chuàng)建串行隊(duì)列。傳入DISPATCH_QUEUE_CONCURRENT表示創(chuàng)建并行隊(duì)列瓷式。
//OBJECTIVE-C//串行隊(duì)列dispatch_queue_tqueue= dispatch_queue_create("tk.bourne.testQueue",NULL);dispatch_queue_tqueue= dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);//并行隊(duì)列dispatch_queue_tqueue= dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);//SWIFT//串行隊(duì)列l(wèi)etqueue= dispatch_queue_create("tk.bourne.testQueue", nil);? letqueue= dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL)//并行隊(duì)列l(wèi)etqueue= dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT)
全局并行隊(duì)列:這應(yīng)該是唯一一個(gè)并行隊(duì)列替饿,只要是并行任務(wù)一般都加入到這個(gè)隊(duì)列。這是系統(tǒng)提供的一個(gè)并發(fā)隊(duì)列贸典。
//OBJECTIVE-Cdispatch_queue_tqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//SWIFTletqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
創(chuàng)建多線程的多種方法
方法一:
NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(mutableThread) object:nil];
方法二:
[NSThread detachNewThreadSelector:@selector(mutableThread) toTarget:self withObject:nil];
方法三:
[self performSelectorInBackground:@selector(mutableThread) withObject:nil];
方法四:多線程blog創(chuàng)建
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
//會(huì)開(kāi)啟一個(gè)多線程
[operationQueue addOperationWithBlock:^{
for(int i = 0; i < 50 ;i++)
{
NSLog(@"多線程:%d",i);
}
}];
方法五:
//相當(dāng)于是一個(gè)線程池
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 1;//設(shè)置并發(fā)數(shù)
//創(chuàng)建線程
NSInvocationOperation *opertion1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread1) object:nil];
//設(shè)置線程的優(yōu)先級(jí)
[opertion1 setQueuePriority:NSOperationQueuePriorityVeryLow];
//創(chuàng)建另一個(gè)線程
NSInvocationOperation *opertion2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread2) object:nil];
[opertion2 setQueuePriority:NSOperationQueuePriorityHigh];
方法六:
dispatch_queue_t queue = dispatch_queue_create("test",NULL);
dispatch_async(queue,^{
for(int i=0;i<50;i++)
{
NSLog(@"多線程:%d",i);
});