進(jìn)程和線程
進(jìn)程:正在運(yùn)行的程序
線程:基本執(zhí)行單元
一個(gè)進(jìn)程至少要一個(gè)線程
一個(gè)進(jìn)程的所有任務(wù)都是在線程中執(zhí)行
一個(gè)線程的任務(wù)是串行的,若一個(gè)線程有多個(gè)任務(wù)則需要按順序一個(gè)一個(gè)執(zhí)行,同一個(gè)時(shí)間內(nèi)豹休,一個(gè)線程只能執(zhí)行一個(gè)任務(wù)
多線程的概念
多線程的執(zhí)行原理:cpu的切換-時(shí)間片
每條線程可以并行執(zhí)行不同的任務(wù)的原理:同一時(shí)間內(nèi),單CPU(單核)只能處理一條線程谆甜,其實(shí)是CUP在多條線程之間調(diào)度擦酌。線程過多蛇更,CPU的負(fù)荷會(huì)過大闺鲸,會(huì)降低效
任務(wù)的同步執(zhí)行&異步執(zhí)行
任務(wù)同步執(zhí)行:一個(gè)接著一個(gè)筋讨,前一個(gè)沒有執(zhí)行完,后面不能執(zhí)行
任務(wù)異步執(zhí)行:任務(wù)同一時(shí)間可以執(zhí)行
多線程在ios開發(fā)中的應(yīng)用:
- 主線程(UI線程):UI操作放到主線程摸恍,不要將耗時(shí)的操作放到主線程中
- 子線程(后臺(tái)線程/非主線程):執(zhí)行耗時(shí)操作
多線程的創(chuàng)建
線程的生命周期:當(dāng)任務(wù)執(zhí)行完成之后被釋放掉
- 線程的基本使用
//獲取主線程
NSThread *mainThread = [NSThread mainThread];
//獲取當(dāng)前線程
NSThread *currentThread = [NSThread currentThread];
//判斷當(dāng)前線程是否是主線程-類/對(duì)象方法
BOOL ismain1= [NSThread isMainThread];
BOOL ismain2 = [currentThread isMainThread];
1悉罕、pthread的基本使用方法:
//創(chuàng)建pthread線程對(duì)象
pthread_t pthreadB;
//創(chuàng)建pthread線程
//參數(shù)一:線程對(duì)象,參數(shù)二:線程的屬性立镶,參數(shù)三:指向函數(shù)的指針蛮粮,參數(shù)四:函數(shù)的參數(shù)
pthread_create(&pthreadB, NULL, task, @"123");
//線程函數(shù)的格式
void *task (void * pagem)
{
NSLog(@"%@-%@",[NSThread currentThread],pagem);
return NULL;
}
2、NSThread方法:OC谜慌,用戶管理周期,創(chuàng)建方法-三種:
//方法一莺奔,object:代表的是函數(shù)的參數(shù)欣范,只有方法一可以返回線程,給線程設(shè)置屬性
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"abc"];
[thread start];
//方法二令哟,分離
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"123"];
//方法三恼琼,后臺(tái)
[self performSelectorInBackground:@selector(run:) withObject:@"ABC"];
//線程調(diào)用方法
//方法一:指定線程去執(zhí)行任務(wù)
[self performSelector:@selector(text2) onThread:self.thread withObject:nil waitUntilDone:YES];
//方法二:放到主線程中
[self performSelectorOnMainThread:@selector(text2) withObject:nil waitUntilDone:YES];
//方法三:在子線程中執(zhí)行任務(wù)
[self performSelectorInBackground:@selector(text2) withObject:nil];
//線程的屬性
//線程的名字
thread.name = @"A";
//線程的優(yōu)先級(jí),范圍在0-1之間屏富,默認(rèn)為0.5
thread1.threadPriority = 1;
//線程的阻塞-兩種方法
//參數(shù)是事件
[NSThread sleepForTimeInterval:2.0];
//參數(shù)是NSDate
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
//線程的強(qiáng)制退出
[NSThread exit];
3晴竞、GCD:C語言編寫的,不需要自己管理生命周期
4狠半、NSOperation:OC噩死,不需要自己管理生命周期
線程的安全性問題:
主線程也是不安全的颤难,所以將所有的UI操作都放到主線程中,使UI操作變得安全
互斥鎖-將需要加鎖的代碼(多條線程搶奪同一個(gè)資源)寫入互斥鎖中已维,token代表唯一的鎖行嗤,一般設(shè)置為self 。
注意: 注意加鎖的位置垛耳。
注意加鎖的條件:多線程共享同一個(gè)資源 栅屏。
注意加鎖會(huì)消耗性能。注意加鎖的結(jié)果會(huì)造成線程的同步(多條線程在同一個(gè)線上執(zhí)行堂鲜,按照順序)栈雳。
@synchronized (<#token#>)
{
}
屬性nonatomic和atomic的區(qū)別:
atomic:原子性,安全(同一時(shí)間只有一個(gè)線程進(jìn)行賦值操作)缔莲,會(huì)給屬性的set方法加鎖哥纫,但是會(huì)消耗資源(其他線程要判斷)-自旋鎖。
nonatomic:非原子性酌予,不安全(同一時(shí)間可以有多個(gè)線程進(jìn)行讀和寫)磺箕,一般建議。
自旋鎖&同步鎖
自旋鎖:如果發(fā)現(xiàn)有其它線程正在鎖定代碼,線程會(huì)用死循環(huán)的方式,一直等待鎖定的代碼執(zhí)行完成,自旋鎖更適合執(zhí)行不耗時(shí)的代碼 抛虫。(只是對(duì)set方法進(jìn)行鎖松靡,不斷的判斷set方法鎖是否打開(當(dāng)set方法是耗時(shí)操作時(shí),會(huì)卡死)atomic)
同步鎖:如果發(fā)現(xiàn)其他線程正在執(zhí)行鎖定代碼,線程會(huì)進(jìn)入休眠(就緒狀態(tài)),等其它線程時(shí)間片到打開鎖后,線程會(huì)被喚醒(執(zhí)行) 建椰。(鎖的范圍沒有定(set和get方法都鎖)雕欺,而且線程是等待,不是一直判斷set方法鎖是否打開)
@synchronized (<#token#>) { }
線程間的通信
方法兩種:
performSelectorOnMainThread:調(diào)轉(zhuǎn)到主線程
performSelector:onThread:調(diào)轉(zhuǎn)到線程(包括主線程)
//參數(shù)一:回到主線程要調(diào)用的哪個(gè)方法
//參數(shù)二:前面方法要傳遞的參數(shù)
//參數(shù)三:是否等待棉姐,YES屠列,先調(diào)轉(zhuǎn)線程的方法,后執(zhí)行原方法
//NO伞矩,先執(zhí)行原方法的然后再執(zhí)行線程的方法
[self performSelectorOnMainThread:@selector(getImage:) withObject:img waitUntilDone:YES];
//簡(jiǎn)寫笛洛,不需要?jiǎng)?chuàng)建getImage:方法,self.imageView中自帶setImage:方法,線程的切換
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:img waitUntilDone:NO];
[self performSelector:@selector(getImage:) onThread:[NSThread mainThread] withObject:img waitUntilDone:YES];
下載圖片:
1乃坤、配置info.plist文件:APP TransportSecurity Settings ->Allow Arbitrary Loads : YES
2苛让、代碼:
NSURL *url = [NSURL URLWithString:@"http://"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.imageView.image = [UIImage imageWithData:data];
獲取時(shí)間差(總結(jié))
//方法一
NSDate *start = [NSDate date];//當(dāng)前的時(shí)間
NSDate *end = [NSDate date];//當(dāng)前的時(shí)間
double interval = [end timeIntervalSinceDate:start];
//方法二:
CFTimeInterval start1 = CFAbsoluteTimeGetCurrent();
CFTimeInterval end1 = CFAbsoluteTimeGetCurrent();
double intervar1 = end1-start1;
GCD
GCD的基本使用-關(guān)鍵dispacth
缺點(diǎn):不能控制開子線程的個(gè)數(shù)
任務(wù)和隊(duì)列
任務(wù):需要的操作
隊(duì)列:存放任務(wù):先進(jìn)先出
函數(shù)
同步函數(shù):只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力-dispatch_sync湿诊,立刻執(zhí)行狱杰,如果我沒有執(zhí)行完畢,那么后面的也不能執(zhí)行
異步函數(shù):可以在新線程中執(zhí)行任務(wù)厅须,具備開啟新線程的能力-dispatch_async仿畸,如果我沒有執(zhí)行完畢,那么后面的也可以執(zhí)行 隊(duì)列
隊(duì)列
- 并發(fā)/行隊(duì)列:
1、以先進(jìn)先出的方式错沽,并發(fā)調(diào)度隊(duì)列中的任務(wù)執(zhí)行
2簿晓、如果當(dāng)前調(diào)度的任務(wù)是同步執(zhí)行的,會(huì)等待任務(wù)執(zhí)行完成后甥捺,再調(diào)度后續(xù)的任務(wù)
3抢蚀、如果當(dāng)前調(diào)度的任務(wù)是異步執(zhí)行的,同時(shí)底層線程池有可用的線程資源镰禾,會(huì)再新的線程調(diào)度后續(xù)任務(wù)的執(zhí)行
- 串行隊(duì)列:
1皿曲、以先進(jìn)先出的方式,順序調(diào)度隊(duì)列中的任務(wù)執(zhí)行
2吴侦、無論隊(duì)列中所指定的執(zhí)行任務(wù)函數(shù)是同步還是異步屋休,都會(huì)等待前一個(gè)任務(wù)執(zhí)行完成后,再調(diào)度后面的任務(wù)
主隊(duì)列 特殊的串行隊(duì)列备韧,代表主線程 不開線程劫樟,同步執(zhí)行 主隊(duì)列特點(diǎn):如果主線程正在執(zhí)行代碼暫時(shí)不調(diào)度任務(wù),等主線程執(zhí)行結(jié)束后在執(zhí)行任務(wù) 主隊(duì)列又叫 全局串行隊(duì)列
任務(wù)和隊(duì)列
同步和異步:能不能開啟新線程,同步不開织堂,異步可以開
并發(fā)和串行:任務(wù)的執(zhí)行方式叠艳,并發(fā)大家一起跑,串行一個(gè)任務(wù)接著一個(gè)任務(wù)執(zhí)行
//創(chuàng)建并發(fā)隊(duì)列易阳,參數(shù)一:C語言的字符串附较,標(biāo)簽DISPATCH_QUEUE_SERIAL代表串行隊(duì)列DISPATCH_QUEUE_CONCURRENT代表并行隊(duì)列
//dispatch_queue_t queue = dispatch_queue_create("com", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建異步任務(wù),添加到隊(duì)列中潦俺,簡(jiǎn)介的寫法
//dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
//獲取全局并發(fā)隊(duì)列拒课,參數(shù)一:優(yōu)先級(jí),DISPATCH_QUEUE_PRIORITY_BACKGROUND 最低,DISPATCH_QUEUE_PRIORITY_DEFAULT 默認(rèn)
//dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//dispatch_get_global_queue(0,0);
隊(duì)列和任務(wù)的執(zhí)行方式
異步函數(shù)+并發(fā)隊(duì)列: 可以創(chuàng)建線程事示,執(zhí)行順序不固定早像,線程可以復(fù)用(優(yōu)化)
異步函數(shù)+同步隊(duì)列: 可以創(chuàng)建線程,只開一個(gè)線程肖爵,在當(dāng)前線程中串行執(zhí)行任務(wù)卢鹦,執(zhí)行順序固定
同步函數(shù)+并發(fā)隊(duì)列: 不可以創(chuàng)建線程,任務(wù)串行
同步函數(shù)+串行隊(duì)列: 不可以創(chuàng)建線程劝堪,任務(wù)串行
主隊(duì)列
//獲取主隊(duì)列,全局同步隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
主隊(duì)列和任務(wù)的執(zhí)行方式
主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列
放到主隊(duì)列的任務(wù)法挨,都會(huì)放到主線程中執(zhí)行
主線程的任務(wù)執(zhí)行完之后,主隊(duì)列才會(huì)調(diào)度主線程去執(zhí)行block的任務(wù)
異步函數(shù)+主隊(duì)列:不會(huì)開子線程幅聘,在主線程中串行執(zhí)行
同步函數(shù)+主隊(duì)列:產(chǎn)生了死鎖(當(dāng)執(zhí)行的當(dāng)前函數(shù)是主線程時(shí))
原因:主隊(duì)列和主線程相互等待會(huì)造成死鎖。 在主線程空閑時(shí)才會(huì)調(diào)度主隊(duì)列中的任務(wù)在主線程執(zhí)行
GCD之間的通信切換
//嵌套函數(shù)
dispatch_sync(queue, ^{
NSURL *url = [NSURL URLWithString:@"http://"];
NSData *data = [NSData dataWithContentsOfURL:url];
//調(diào)轉(zhuǎn)到主線程中
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:data];
});
});
GCD的常用函數(shù)
延時(shí):
方法一:
[self performSelector:@selector(say) withObject:self afterDelay:2];
方法二:
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(say) userInfo:nil repeats:nil];
方法三:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
一次性代碼
- 在程序的生命周期中只執(zhí)行一次窃植,不能放到懶加載中
dispatch_once_t once ;
dispatch_once(&once, ^{
NSLog(@"------");
});
自動(dòng)釋放池的使用
1帝蒿、當(dāng)創(chuàng)建了許多臨時(shí)變量的時(shí)候,需要加自動(dòng)釋放池
2巷怜、開啟子線程的時(shí)候也需要加自動(dòng)釋放池