多線(xiàn)程原理:
- 同一時(shí)間CPU只能處理一條線(xiàn)程, 只有一條線(xiàn)程在工作
- 多線(xiàn)程并發(fā)執(zhí)行,起始是CPU在各個(gè)線(xiàn)程之間快速調(diào)度的結(jié)果
- 由于CPU調(diào)度線(xiàn)程速度非???,所以就造成了多線(xiàn)程并發(fā)的假象
一般情況下耗時(shí)操作放在子線(xiàn)程里面,多線(xiàn)程也正是解決耗時(shí)操作,防止卡住主線(xiàn)程產(chǎn)生的。
主線(xiàn)程
- 程序已啟動(dòng)就自動(dòng)創(chuàng)建的線(xiàn)程就是主線(xiàn)程
- 作用一般就是相應(yīng)用戶(hù)點(diǎn)擊事件,刷新UI等等
多線(xiàn)程的實(shí)現(xiàn)方案

Paste_Image.png
- NSThread
@Parmark 第一中創(chuàng)建方法
//創(chuàng)建線(xiàn)程
//在內(nèi)存中開(kāi)辟空間
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"thread"];
thread.name = @"my-thread";
//啟動(dòng)線(xiàn)程 并且系統(tǒng)會(huì)把這個(gè)線(xiàn)程放到可調(diào)度程序池里面 為了方便CPU來(lái)回調(diào)用
[thread start];
//任務(wù)執(zhí)行完畢后 會(huì)自動(dòng)銷(xiāo)毀線(xiàn)程
- (void)run:(NSString *)param
{
//處理耗時(shí)操作
}
@Parmark 第二中創(chuàng)建方法
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"thread"];
- (void)run:(NSString *)param
{
//處理耗時(shí)操作
}
@Parmark 第三中創(chuàng)建方法
[self performSelectorInBackground:@selector(run:) withObject:@"thread"];
- (void)run:(NSString *)param
{
//處理耗時(shí)操作
}
//卡住線(xiàn)程睡兩秒 控制線(xiàn)程進(jìn)入阻塞狀態(tài)
[NSThread sleepForTimeInterval:2.0];
//退出線(xiàn)程(強(qiáng)制性的)
[NSThread exit];
線(xiàn)程安全
- 資源共享:一塊資源 被多個(gè)線(xiàn)程共享 也就是多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一塊資源
隱患:

Paste_Image.png
解決引號(hào) -- > 加把互斥鎖

Paste_Image.png
代碼:
###沒(méi)加互斥鎖的代碼
self.ticketCount = 100;//100張票
self.thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread1.name = @"售票員1";
self.thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread2.name = @"售票員2";
self.thread3 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread3.name = @"售票員3";
- (void)saleTicket
{
while (1) {
//先取出總數(shù)
NSInteger count = self.ticketCount;
if (count > 0) {
self.ticketCount = count - 1;
NSLog(@"%@賣(mài)了一張票 -- 還剩下%ld張票",[NSThread currentThread].name,_ticketCount);
}else{
NSLog(@"票已經(jīng)賣(mài)完了");
break;
}
}
}
###加互斥鎖的代碼
- (void)saleTicket
{
//用同一把鎖 可以記錄線(xiàn)程的狀態(tài)
while (1) {
@synchronized (self) {//加鎖
//先取出總數(shù)
NSInteger count = self.ticketCount;
if (count > 0) {
self.ticketCount = count - 1;
NSLog(@"%@賣(mài)了一張票 -- 還剩下%ld張票",[NSThread currentThread].name,_ticketCount);
}else{
NSLog(@"票已經(jīng)賣(mài)完了");
break;
}
}
}
}
線(xiàn)程間的通訊
//開(kāi)辟一個(gè)子線(xiàn)程
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelectorInBackground:@selector(downLoad) withObject:nil];
}
//子線(xiàn)程要做的事
- (void)downLoad
{
NSURL * url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/5ab5c9ea15ce36d358d27ee43ef33a87e850b114.jpg"];
//下載圖片
NSData * data = [NSData dataWithContentsOfURL:url];
//生成圖片
UIImage * image = [UIImage imageWithData:data];
//回到主線(xiàn)程刷新UI
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}
GCD的基本使用
- 任務(wù): 執(zhí)行操作
- 隊(duì)列: 存放任務(wù)
- 隊(duì)列的類(lèi)型
- 并發(fā)隊(duì)列 (只有在)dispatch_async下才有效
- 串行隊(duì)列
同步 - 異步:主要影響是能不能開(kāi)新線(xiàn)程
- 同步只能在當(dāng)前線(xiàn)程執(zhí)行任務(wù),不能開(kāi)啟新線(xiàn)程
- 異步可以在新的線(xiàn)程中執(zhí)行任務(wù),能開(kāi)啟新線(xiàn)程
串行 - 并發(fā):主要影響任務(wù)的執(zhí)行方式
- 串行:一個(gè)任務(wù)執(zhí)行完畢 執(zhí)行下一個(gè)任務(wù)
- 并發(fā):多個(gè)任務(wù)同時(shí)執(zhí)行
使用步驟
- 定制任務(wù):確定想要做的事情
- 將任務(wù)添加到隊(duì)列中
- GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出來(lái),放到對(duì)應(yīng)的線(xiàn)程中執(zhí)行
- 任務(wù)的去除遵循:先進(jìn)先出 后進(jìn)后出 的原則
代碼:
//異步線(xiàn)程
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
//同步線(xiàn)程
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
###異步函數(shù)+并發(fā)隊(duì)列 (可以開(kāi)啟多條線(xiàn)程 并且可以同時(shí)執(zhí)行)
//創(chuàng)建一個(gè)并發(fā)隊(duì)列 DISPATCH_QUEUE_CONCURRENT:隊(duì)列類(lèi)型
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);
//將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
#@pargam 或者這種寫(xiě)法
//獲取全局隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###同步函數(shù)+并發(fā)隊(duì)列 (不會(huì)開(kāi)啟新的線(xiàn)程)
//獲得全局的并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函數(shù)
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###異步函數(shù)+串行對(duì)列 (可以開(kāi)線(xiàn)程 但是不能同時(shí)執(zhí)行)
//串行隊(duì)列 沒(méi)有全局的 只能手動(dòng)創(chuàng)建
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###同步函數(shù)+串行對(duì)列 (不可以開(kāi)線(xiàn)程 )
//串行隊(duì)列 沒(méi)有全局的 只能手動(dòng)創(chuàng)建
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
主隊(duì)列
- GCD自帶的一種特殊的串行隊(duì)列
- 放到主隊(duì)列的任務(wù)都會(huì),在主線(xiàn)程中執(zhí)行
- 使用dispatch_get_main_queue()獲取主隊(duì)列
###主隊(duì)列+異步函數(shù)(只會(huì)在主線(xiàn)程中執(zhí)行任務(wù))
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###主隊(duì)列+同步函數(shù)(線(xiàn)程沖突 不會(huì)執(zhí)行任何操作)
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
各種隊(duì)列的執(zhí)行效果

Paste_Image.png
GCD的線(xiàn)程之間通訊
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL * url = [NSURL URLWithString:@"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1479864304&di=99dcf40127f2dc4273536f73d0951638&src=http://d.hiphotos.baidu.com/image/pic/item/2e2eb9389b504fc2065e2bd2e1dde71191ef6de0.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage * image = [UIImage imageWithData:data];
//回到主線(xiàn)程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
GCD中還有另外一個(gè)執(zhí)行任務(wù)的函數(shù)
//在它前面的函數(shù)執(zhí)行完畢才執(zhí)行它的任務(wù),在它后面的函數(shù)在它執(zhí)行完畢后才開(kāi)始執(zhí)行
dispatch_barrier_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
iOS延時(shí)執(zhí)行的函數(shù)
//延時(shí)兩秒執(zhí)行run方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//執(zhí)行的操作
});
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//一次性函數(shù)
//此函數(shù)只執(zhí)行一次
});
GCD隊(duì)列組
- 用隊(duì)列組下載多張圖片并且合成一張圖片
//創(chuàng)建一個(gè)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建一個(gè)隊(duì)列組
dispatch_group_t group = dispatch_group_create();
//1.下載圖片1
dispatch_group_async(group, queue, ^{
NSURL * url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/h%3D200/sign=4241e02c86025aafcc3279cbcbecab8d/562c11dfa9ec8a13f075f10cf303918fa1ecc0eb.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage * image = [UIImage imageWithData:data];
self.image1 = image;
});
//2.下載圖片2
dispatch_group_async(group, queue, ^{
NSURL * url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/8cb1cb134954092359d94e479758d109b3de4952.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage * image = [UIImage imageWithData:data];
self.image2 = image;
});
//3.將圖片1和圖片2合成一張新的圖片
dispatch_group_notify(group, queue, ^{
//能保證組里面的任務(wù)都完成了
//能來(lái)到這里說(shuō)明前兩張圖片一定下載完了
//開(kāi)啟圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(336, 440));
//繪制圖片
[self.image1 drawInRect:CGRectMake(0, 0, 168, 220)];
[self.image2 drawInRect:CGRectMake(168, 0, 168, 220)];
//獲取上下文的圖片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
//結(jié)束上下文
UIGraphicsEndImageContext();
//回到主線(xiàn)程顯示圖片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
//4.將合成后的圖片顯示出來(lái)
GCD實(shí)現(xiàn)單利
###第一種方式
static Person * _person;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone: zone];
});
return _instance;
}
+ (instancetype)defaultManger
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_person = [[self alloc]init];
});
return _person;
}
//記得遵守NSCopying協(xié)議
//實(shí)現(xiàn)此方法為了保證copy的時(shí)候 訪(fǎng)問(wèn)的是同一個(gè)對(duì)象
- (id)copyWithZone:(NSZone *)zone
{
return _person;
}
###第二種方式
static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized (self) {//加鎖 防止多線(xiàn)程訪(fǎng)問(wèn)出問(wèn)題
if (_instance == nil)
{
_instance = [self allocWithZone:zone];
}
}
return _instance;
}
+ (instancetype)sharedInstance
{
@synchronized (self) {//加鎖 防止多線(xiàn)程訪(fǎng)問(wèn)出問(wèn)題
if (_instance == nil)
{
_instance = [[self alloc]init];
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}