iOS開發(fā)筆記-多線程01
理論部分:
一.進程
1)概念:是指在系統(tǒng)中正在運行的一個應用程序,每個進程的存儲空間是獨立的(終端中輸入top可以查看進程,按Q退出)
P.S.進程與應用程序的差別:進程是有狀態(tài)的,正在執(zhí)行的
2)進程是CPU進行資源分配與調(diào)度的基本單位
二.線程
1)線程是CPU調(diào)度的基本單位(1個進程想執(zhí)行任務,必須要有線程 ),一個進程中所有的任務都在線程中執(zhí)行
2)一個線程中的任務的執(zhí)行是串行的
P.S.進程與線程的辨析
1)一個程序可以對應多個進程,一個進程中可以有多個線程,但至少要有一個線程,線程是進程的一條路徑
2)同一個進程內(nèi)的線程共享進程的資源
3)進程可以分配資源,而線程不可以
3)多線程并發(fā)執(zhí)行:其實是CPU快速地在多個線程之間調(diào)度(只是假象,因為同一時間,CPU只能處理一條線程),建議開辟線程數(shù)量為3-5條
4)主線程:也稱為UI線程,作用是:1.顯示\刷新UI界面 2.處理UI事件 (所有和UI相關的操作都需要在主線程中執(zhí)行)
打印時,若顯示的number等于1,則為主線程,其他數(shù)字,則為子線程
P.S.主線程注意點
1.不要將耗時的任務放在主線程
2.UI刷新操作要放在主線程中
3.耗時的操作放在子線程中(后臺線程)
三.pthread
說明:pthread的基本使用(需要包含頭文件)
//使用pthread創(chuàng)建線程對象
pthread_t thread;
//使用pthread創(chuàng)建線程
//第一個參數(shù):線程對象地址
//第二個參數(shù):線程屬性
//第三個參數(shù):指向函數(shù)的指針
//第四個參數(shù):傳遞給該函數(shù)的參數(shù)
pthread_create(&thread, NULL, run, NULL);
四.NSThread
1.一個NSThread代表一條線程
2.優(yōu)先級高,cpu調(diào)用的概率高(影響的也是CPU調(diào)度到的概率)
1)NSThread創(chuàng)建線程的四種方式
//第一種創(chuàng)建線程的方式:alloc initWithTarget.
//特點:需要手動開啟線程购公,可以拿到線程對象進行詳細設置
//創(chuàng)建線程
/*
第一個參數(shù):目標對象
第二個參數(shù):選擇器梆造,線程啟動要調(diào)用哪個方法
第三個參數(shù):前面方法要接收的參數(shù)(最多只能接收一個參數(shù)魄缚,沒有則傳nil)
*/
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"hmx"];
//啟動線程
[thread start];
//第二種創(chuàng)建線程的方式:分離出一條子線程
//特點:自動啟動線程,無法對線程進行更詳細的設置
/*
第一個參數(shù):線程啟動調(diào)用的方法
第二個參數(shù):目標對象
第三個參數(shù):傳遞給調(diào)用方法的參數(shù)
*/
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分離出來的子線程"];
//第三種創(chuàng)建線程的方式:后臺線程
//特點:自動啟動線程梗醇,無法進行更詳細設置
[self performSelectorInBackground:@selector(run:) withObject:@"我是后臺線程"];
//第四種創(chuàng)建線程方法:alloc init
//新建一個類 繼承自NSThread,重寫內(nèi)部的main方法來封裝任務
NSThread *thread = [[NSThread alloc]init];
//啟動線程
[thread start];
2)設置線程的屬性
//設置線程的名稱
thread.name = @"線程A";
//設置線程的優(yōu)先級,注意線程優(yōu)先級的取值范圍為0.0~1.0之間,1.0表示線程的優(yōu)先級最高,如果不設置該值撒蟀,那么理想狀態(tài)下默認為0.5
thread.threadPriority = 1.0;
3.線程狀態(tài)相關問題:
1.線程的各種狀態(tài):新建-就緒-運行-阻塞-死亡
2.當線程的任務執(zhí)行完畢之后就銷毀了
3.當線程放入可調(diào)度線程池中,CPU才會調(diào)度
4.線程已經(jīng)死了,是不能再重新打開的
5.當線程解除阻塞狀態(tài)時,會進入就緒狀態(tài),而不是運行狀態(tài)
4.線程安全相關問題:
1. 互斥鎖:@synchronized(self){//需要鎖定的代碼}(推薦使用self)
2. 前提條件:多個線程可能會訪問同一塊資源
3. 注意點:1)要注意加鎖的位置
2)鎖對象必須是對象,且全局唯一
3)加上互斥鎖之后,就會使線程同步(即線程永遠都是按一個順序調(diào)用,例:一開始是2->1->3的順序,之后依舊為2->1->3的順序)
4)線程是需要消耗性能的
4. 專業(yè)術語-線程同步
5. 原子和非原子屬性(是否對setter方法加鎖)
atomic 線程安全,需要消耗大量資源
nonatomic 非線程安全,適合內(nèi)存小的移動設備 (開發(fā)中聲明為這個)
5.計算代碼段間的執(zhí)行時間
//第一種方法
NSDate *start = [NSDate date];
//2.根據(jù)url地址下載圖片數(shù)據(jù)到本地(二進制數(shù)據(jù))
NSData *data = [NSData dataWithContentsOfURL:url];
NSDate *end = [NSDate date];
NSLog(@"第二步操作花費的時間為%f",[end timeIntervalSinceDate:start]);
//第二種方法
CFTimeInterval start = CFAbsoluteTimeGetCurrent();
NSData *data = [NSData dataWithContentsOfURL:url];
CFTimeInterval end = CFAbsoluteTimeGetCurrent();
NSLog(@"第二步操作花費的時間為%f",end - start);
6.回到主線程刷新UI
//4.1 第一種方式
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
//4.2 第二種方式
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//4.3 第三種方式
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}
五.PCD
1.GCD基本知識
1) 兩個核心概念:隊列和任務:
隊列:用來存放任務(決定在哪個線程執(zhí)行任務)
任務:執(zhí)行什么操作
2) 同步函數(shù)和異步函數(shù)的區(qū)別:
同步:1.只能在當前線程中執(zhí)行,不具備開啟新線程的能力
2.執(zhí)行任務的方式:當執(zhí)行到我時必須等我執(zhí)行完才能執(zhí)行后面的任務
異步:1.可以在新的線程中執(zhí)行任務,具備開啟新線程的能力
2.執(zhí)行任務的方式:可以不用等我執(zhí)行完畢,就可以直接執(zhí)行后面的任務
3) 串行隊列:1.取出一個任務后,等到該任務執(zhí)行完畢之后,接著去第二個任務
2.創(chuàng)建方式:a.自己創(chuàng)建 b.主隊列
并行隊列:1.取出一個任務后,接著執(zhí)行第二個任務
2.創(chuàng)建方式:a.自己創(chuàng)建 b.全局并發(fā)隊列
主隊列:1.在安排任務的時候,會先檢查主線程的狀態(tài),如果主線程忙,那么久暫停調(diào)度直到空閑為止
2.想要實現(xiàn)控制系統(tǒng)開幾條線程時,只需要控制創(chuàng)建幾個隊列
3.凡是放在主隊列里的任務都在主線程完成
2.GCD基本使用【重要】
01 異步函數(shù)+并發(fā)隊列:開啟多條線程叙谨,并發(fā)執(zhí)行任務
02 異步函數(shù)+串行隊列:開啟一條線程,串行執(zhí)行任務
03 同步函數(shù)+并發(fā)隊列:不開線程保屯,串行執(zhí)行任務
04 同步函數(shù)+串行隊列:不開線程手负,串行執(zhí)行任務
05 異步函數(shù)+主隊列:不開線程,在主線程中串行執(zhí)行任務
06 同步函數(shù)+主隊列:不開線程姑尺,串行執(zhí)行任務(注意死鎖發(fā)生)
P.S若當前是子線程執(zhí)行的同步函數(shù)加上主隊列的方式,不會發(fā)生死鎖
07 注意同步函數(shù)和異步函數(shù)在執(zhí)行順序上面的差異
3.GCD的線程間通信:
//0.獲取一個全局的隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.先開啟一個線程竟终,把下載圖片的操作放在子線程中處理
dispatch_async(queue, ^{
//2.下載圖片
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"下載操作所在的線程--%@",[NSThread currentThread]);
//3.回到主線程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
//打印查看當前線程
NSLog(@"刷新UI---%@",[NSThread currentThread]);
});
});
4.GCD相關注意點
1. 開線程的兩個條件:1.必須是異步函數(shù) 2.必須不是主隊列(主線程)
2. 放到主隊列里的任務,必須要在主線程中執(zhí)行(不一定在當前線程中執(zhí)行,即即使在子線程中調(diào)用了主隊列,還是子主線程中調(diào)用)
5.GCD常用函數(shù):
1)柵欄函數(shù)(控制任務的執(zhí)行順序)
dispatch_barrier_async(queue, ^{
NSLog(@"--dispatch_barrier_async-");
});
/*
P.S 注意點:1.柵欄函數(shù)可以控制線程的執(zhí)行順序
2.柵欄函數(shù)不能使用全局并發(fā)隊列
3.柵欄函數(shù)在執(zhí)行時是獨占的
4.dispatch_barrier_async == dispatch_async 這兩個是等同的
*/
2)延遲執(zhí)行(延遲·控制在哪個線程執(zhí)行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"---%@",[NSThread currentThread]);
});
/*
Q:GCD的延遲執(zhí)行 是先等2秒再提交 OR 先提交再等2秒?
A:先等2秒再提交(延遲提交) 因為任務提交到隊列里就不好控制了
*/
3)一次性代碼(注意不能放到懶加載)
-(void)once
{
//整個程序運行過程中只會執(zhí)行一次
//onceToken用來記錄該部分的代碼是否被執(zhí)行過
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
}
/*
一次性代碼的特點:1.整個程序運行過程中只會執(zhí)行一次
2.它本身是線程安全的
*/
4)快速迭代(開多個線程并發(fā)完成迭代操作)
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
/*
快速迭代:多個線程(子線程與主線程一起工作的)一起并發(fā)執(zhí)行任務的--->對順序沒有要求時使用
Q:快速迭代若不是從0開始,怎么處理
A:初始值若不是0,則不需要使用GCD的快速迭代,使用for循環(huán)即可
*/
5)隊列組(同柵欄函數(shù))--->調(diào)度組
//創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
//隊列組中的任務執(zhí)行完畢之后,執(zhí)行該函數(shù)
dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);//這個方法本身也是異步的
/*
方便管理一個組內(nèi)的操作
*/
6)進入群組和離開群組
dispatch_group_enter(group);//執(zhí)行該函數(shù)后切蟋,后面異步執(zhí)行的block會被gruop監(jiān)聽
dispatch_group_leave(group);//異步block中统捶,所有的任務都執(zhí)行完畢,最后離開群組
/*
注意:dispatch_group_enter|dispatch_group_leave必須成對使用
當需要監(jiān)聽多個任務時,則重復寫一組即可
*/
//死等方法:知道隊列組中所有都執(zhí)行完畢之后才過掉該方法(同步)
//diepatch_group_wait(group,DISPATCH_TIME_FOREVER)