今天被人問到了下多線程方面的一些知識(shí),平時(shí)一直在用然后原 理不是理解得很透徹,所以今晚決定整理資料來個(gè)掃盲貼.開始前我們先來個(gè)基礎(chǔ)知識(shí)講解吧.
![Uploading 多線程_837987.jpg . . .]
首先開始我們先區(qū)別下進(jìn)程與線程的區(qū)別.
***
進(jìn)程:系統(tǒng)每個(gè)程序運(yùn)行都需要一個(gè)進(jìn)程,進(jìn)程間是在其專用且
受保護(hù)的內(nèi)存空間內(nèi)獨(dú)立的
線程:1)線程為進(jìn)程能夠正常運(yùn)作提供了條件,且每個(gè)進(jìn)程最少得一條線程.
2)所謂進(jìn)程通俗講就是我們系統(tǒng)運(yùn)行的程序,進(jìn)程的基本執(zhí)行單元為線程.
線程串行:
在單線程中執(zhí)行任務(wù)是需要排隊(duì)的,你如果要執(zhí)行多人任務(wù),那么就只能按順序執(zhí)行這些任務(wù);就像去超市排隊(duì)結(jié)賬,你要等別 人結(jié)賬完才能輪到你
多線程
廢話不多說先來了解下什么是多線程先
***基本概念***
多線程就是給單前的進(jìn)程(APP/程序)開多條線程就是開幾條水管好排水,每條水管執(zhí)行不同的Task.
***線程的并行***
所謂并行就是你像燒水的時(shí)候可以順便干點(diǎn)其它東西,大家互相互執(zhí)行不同的東西
說白了就是你的CPU在多線線程中來回工作,就是這條線程干點(diǎn)那條線程干點(diǎn)
***多線程優(yōu)點(diǎn)與缺點(diǎn) ***
缺點(diǎn):1)開多線程就是一個(gè)人在進(jìn)行多個(gè)任務(wù)一樣,這樣CPU開銷會(huì)很大,太多線程其實(shí)會(huì)影響性能的
2)這樣程序會(huì)設(shè)計(jì)的比較復(fù)雜,在數(shù)據(jù)共享與線程(APP)間通信實(shí)現(xiàn)難度很大
3)加重內(nèi)存開銷(默認(rèn)主線程是1M,子線程是512KB)
優(yōu)點(diǎn):1)目前CPU的處理效率很高都是多核多核一定程度上提高資源的利用率(內(nèi)存/CPU)
2)提高程序執(zhí)行的效率了,就是你燒開水去切菜一樣提高效率節(jié)省時(shí)間.
在iOS開發(fā)中的用處
寶貝說再好還不如來來點(diǎn)實(shí)在的對(duì)吧.
a)主線程
1.一個(gè)APP運(yùn)行后在iOS系統(tǒng)中默認(rèn)就是在主線程中運(yùn)行的我們把它稱為"主線程"或者"UI線程"
2.刷新顯示界面,處理用戶交互事件
坑點(diǎn)!!!:1)如果是在對(duì)一些比較占用資源運(yùn)行時(shí)間較長的耗時(shí)操作記住要開子線程,要不你的APP就卡死啦
2)和用戶交互的刷新操作一定要放在主線程,要不一些執(zhí)著的用戶會(huì)以為貴公司APP垃圾辣雞就卸了.寶寶心里苦
主題幾種多線程實(shí)現(xiàn)方案
***程序員管理的生命***
pthread說明:惡心的C語言API,我就不想用了,用起來好難,線程的生死還要我們程序員覺得,一不小心就崩了程序崩了知道不.
特點(diǎn):1)可用在Unix\Linux\Windows主流系統(tǒng)可以跨平臺(tái)開發(fā)
2)通用的API就是大家都可以用,公共廁所
NSThread特點(diǎn):1)這個(gè)東西我們偶爾使用
2)偉大的OOP類
3)還是得碼農(nóng)解決生死
***系統(tǒng)自動(dòng)管理***
GCD特點(diǎn):1)為了替代NSThread等需要人工判斷生死的東西
2)為了不浪費(fèi)多核的CPU資源
3)還是C語言,使用難道大大的難
NSOperation特點(diǎn):1)OOP語言,系統(tǒng)會(huì)自動(dòng)管理
2)基于GCD底層的,比GCD使用方便還新添一些簡單的功能
重點(diǎn)了解OOP語言中的NSTHread
基本使用:創(chuàng)建的3種方式
1)自動(dòng)啟動(dòng),不能管理內(nèi)容設(shè)置
[self performSelectorInBackground:@selector(run:) withObject:@"I am 后臺(tái)線程"];
2)自動(dòng)啟動(dòng),分離出一條子線程,還是不能管理內(nèi)容設(shè)置
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分離出來的子線程"];
***推薦使用***
3)可以管理內(nèi)容設(shè)置但是需要手動(dòng)開啟線程.
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"wendingding"];
//啟動(dòng)線程
[thread start];
其它一些屬性使用:
1)屬性名稱:thread.name = @"快給我起名字";
2)優(yōu)先級(jí)(默認(rèn)0.5):thread.threadPriority = 1.0;
線程safety:1)多線程同時(shí)操作同一個(gè)數(shù)據(jù)會(huì)發(fā)生死鎖情況,需要加互斥所,相關(guān)代碼@synchronized(self){}
2)原子性與非原子屬性(是否對(duì)setter方法加鎖), 線程同步與同步線程區(qū)別
線程同步:多個(gè)線程操作同一個(gè)數(shù)據(jù)資源提供競爭的解決方案
同步線程:就是排隊(duì)串行執(zhí)行任務(wù)的線程
線程狀態(tài):[NSThread exit]//退出當(dāng)前的線程
[NSThread sleepForTimeInterval:2.0];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
//:線程掛了是不能復(fù)生的
線程間通信:
-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
// [self download2];
//開啟一條子線程來下載圖片
[NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}
-(void)downloadImage
{
//1.確定要下載網(wǎng)絡(luò)圖片的url地址,一個(gè)url唯一對(duì)應(yīng)著網(wǎng)絡(luò)上的一個(gè)資源
NSURL *url = [NSURL URLWithString:@"http://www.reibang.com/p/e09c44c5ab12"];
//2.根據(jù)url地址下載圖片數(shù)據(jù)到本地(二進(jìn)制數(shù)據(jù)
NSData *data = [NSData dataWithContentsOfURL:url];
//3.把下載到本地的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成圖片
UIImage *image = [UIImage imageWithData:data];
//4.回到主線程刷新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];
}
計(jì)算代碼時(shí)間:
1)CFTimeInterval start = CFAbsoluteTimeGetCurrent();
NSData *data = [NSData dataWithContentsOfURL:url];
CFTimeInterval end = CFAbsoluteTimeGetCurrent();
NSLog(@"操作花費(fèi)的時(shí)間為%f",end - start);
2)NSDate *start = [NSDate date];
//2.根據(jù)url地址下載圖片數(shù)據(jù)到本地(二進(jìn)制數(shù)據(jù))
NSData *data = [NSData dataWithContentsOfURL:url];
NSDate *end = [NSDate date];
NSLog(@"第二步操作花費(fèi)的時(shí)間為%f",[end timeIntervalSinceDate:start]);
學(xué)習(xí)重點(diǎn) GCD
基本知識(shí):
1)同步/異步函數(shù)
2)隊(duì)列/任務(wù)理解
使用重點(diǎn):
01 異步函數(shù)+并發(fā)隊(duì)列:開啟多條線程,并發(fā)執(zhí)行任務(wù)
02 異步函數(shù)+串行隊(duì)列:開啟一條線程前酿,串行執(zhí)行任務(wù)
03 同步函數(shù)+并發(fā)隊(duì)列:不開線程陌宿,串行執(zhí)行任務(wù)
04 同步函數(shù)+串行隊(duì)列:不開線程涝缝,串行執(zhí)行任務(wù)
05 異步函數(shù)+主隊(duì)列:不開線程窑眯,在主線程中串行執(zhí)行任務(wù)
06 同步函數(shù)+主隊(duì)列:不開線程梭伐,串行執(zhí)行任務(wù)(注意死鎖發(fā)生)
07 注意同步函數(shù)和異步函數(shù)在執(zhí)行順序上面的差異
GCD中線程間通信:
//0.獲取一個(gè)全局的隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.先開啟一個(gè)線程获黔,把下載圖片的操作放在子線程中處理
dispatch_async(queue, ^{
//2.下載圖片
NSURL *url = [NSURL URLWithString:@"http://www.baidu.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;
//打印查看當(dāng)前線程
NSLog(@"刷新UI---%@",[NSThread currentThread]);
});
});
GCD中其它法寶
1)柵欄函數(shù)(控制任務(wù)的執(zhí)行順序)
dispatch_barrier_async(queue, ^{
NSLog(@"柵欄函數(shù)");
});
2)延遲執(zhí)行(延遲·控制在哪個(gè)線程執(zhí)行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"---%@",[NSThread currentThread]);
});
3)一次性代碼(注意不能放到懶加載)
-(void)onceToken
{
//整個(gè)程序運(yùn)行過程中只會(huì)執(zhí)行一次
//onceToken用來記錄該部分的代碼是否被執(zhí)行過
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
}
4)快速迭代(開多個(gè)線程并發(fā)完成迭代操作)
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
5)隊(duì)列組(同柵欄函數(shù))
//創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
//隊(duì)列組中的任務(wù)執(zhí)行完畢之后蚀苛,執(zhí)行該函數(shù)
dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
6)進(jìn)入群組和離開群組
dispatch_group_enter(group);//執(zhí)行該函數(shù)后,后面異步執(zhí)行的block會(huì)被gruop監(jiān)聽
dispatch_group_leave(group);//異步block中玷氏,所有的任務(wù)都執(zhí)行完畢堵未,最后離開群組
//注意:dispatch_group_enter|dispatch_group_leave必須成對(duì)使用