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.開發(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)度速度非??焖?,所以感覺是一起執(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、開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能
b、線程越多,CPU在調(diào)度線程上的開銷就越大
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,聽名字就霸氣。它是蘋果為多核的并行運(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í)行開很多線程,一起執(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ì)開啟一個(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);
});