iOS中常見(jiàn)的多線程方案
控制器命令:
c:過(guò)掉
step:輸出匯編
stepi:進(jìn)入這個(gè)匯編函數(shù) 簡(jiǎn)稱si

- NSThread、GCD、NSOperation的底層實(shí)現(xiàn)其實(shí)都是pthread
GCD的常用函數(shù)

GCD的隊(duì)列

容易混淆的術(shù)語(yǔ):同步、異步,并發(fā)、串行

各種隊(duì)列的執(zhí)行效果
// dispatch_sync和dispatch_async用來(lái)控制是否要開(kāi)啟新的線程
/**
隊(duì)列的類型,決定了任務(wù)的執(zhí)行方式(并發(fā)、串行)
1.并發(fā)隊(duì)列
2.串行隊(duì)列
3.主隊(duì)列(也是一個(gè)串行隊(duì)列)
*/

面試題
1、下列問(wèn)題是否會(huì)產(chǎn)生死鎖
- (void)interview01
{
// 問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務(wù)2");
});
NSLog(@"執(zhí)行任務(wù)3");
// dispatch_sync立馬在當(dāng)前線程同步執(zhí)行任務(wù)
}
- (void)interview02
{
// 問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?不會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務(wù)2");
});
NSLog(@"執(zhí)行任務(wù)3");
// dispatch_async不要求立馬在當(dāng)前線程同步執(zhí)行任務(wù)
// 打印順序:任務(wù)1-3-2
}
- (void)interview03
{
// 問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
NSLog(@"執(zhí)行任務(wù)2");
dispatch_sync(queue, ^{ // 1
NSLog(@"執(zhí)行任務(wù)3");
});
NSLog(@"執(zhí)行任務(wù)4");
});
NSLog(@"執(zhí)行任務(wù)5");
// block0等block1執(zhí)行完畢,block1等block0執(zhí)行完畢
// 打印順序:任務(wù)1-5-2-死鎖
}
- (void)interview04
{
// 問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?不會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
NSLog(@"執(zhí)行任務(wù)2");
dispatch_sync(queue2, ^{ // 1
NSLog(@"執(zhí)行任務(wù)3");
});
NSLog(@"執(zhí)行任務(wù)4");
});
NSLog(@"執(zhí)行任務(wù)5");
// 打印順序:任務(wù)1-5-2-3-4
}
- (void)interview05
{
// 問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?不會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // 0
NSLog(@"執(zhí)行任務(wù)2");
dispatch_sync(queue, ^{ // 1
NSLog(@"執(zhí)行任務(wù)3");
});
NSLog(@"執(zhí)行任務(wù)4");
});
NSLog(@"執(zhí)行任務(wù)5");
}
- 隊(duì)列的特點(diǎn):排隊(duì),F(xiàn)IFO,F(xiàn)irst In First Out,先進(jìn)先出
- dispatch_sync:立馬在當(dāng)前線程執(zhí)行任務(wù),執(zhí)行完成才能繼續(xù)往下執(zhí)行
- dispatch_async不要求立馬在當(dāng)前線程同步執(zhí)行任務(wù)
2、下面代碼的打印結(jié)果是什么?

// 這句代碼的本質(zhì)是往Runloop中添加定時(shí)器
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
// 這句代碼本質(zhì)就是Runtime調(diào)用objc_msgSend
[self performSelector:@selector(test) withObject:nil];
3、下面打印結(jié)果是什么?
- (void)test
{
NSLog(@"2");
}
// 打印結(jié)果為1,因?yàn)閇thread start]啟動(dòng)會(huì)運(yùn)行當(dāng)前block,輸出結(jié)果為1,但是輸出以后,線程也就自動(dòng)退出了,也就是沒(méi)有子線程了,所以thread=nil,如果想繼續(xù)執(zhí)行的話 在block里面啟動(dòng)RunLoop。
// [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1");
// [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
GNUstep
- GNUstep是GNU計(jì)劃的項(xiàng)目之一,它將Cocoa的OC庫(kù)重新開(kāi)源實(shí)現(xiàn)了一遍
- 源碼地址:http://www.gnustep.org/resources/downloads.php
- 雖然GNUstep不是蘋(píng)果官方源碼,但還是具有一定的參考價(jià)值
隊(duì)列組的使用
思考:如何用gcd實(shí)現(xiàn)以下功能
異步并發(fā)執(zhí)行任務(wù)1、任務(wù)2
等任務(wù)1、任務(wù)2都執(zhí)行完畢后,再回到主線程執(zhí)行任務(wù)3
// 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("my_queue", DISPATCH_QUEUE_CONCURRENT);
// 添加異步任務(wù)
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)1-%@", [NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)2-%@", [NSThread currentThread]);
}
});
// 等前面的任務(wù)執(zhí)行完畢后,會(huì)自動(dòng)執(zhí)行這個(gè)任務(wù)
// dispatch_group_notify(group, queue, ^{
// dispatch_async(dispatch_get_main_queue(), ^{
// for (int i = 0; i < 5; i++) {
// NSLog(@"任務(wù)3-%@", [NSThread currentThread]);
// }
// });
// });
//也可以直接把主隊(duì)列放進(jìn)去
// dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// for (int i = 0; i < 5; i++) {
// NSLog(@"任務(wù)3-%@", [NSThread currentThread]);
// }
// });
// 也可以執(zhí)行完任務(wù)1、2 再去執(zhí)行任務(wù)3、4
dispatch_group_notify(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)3-%@", [NSThread currentThread]);
}
});
dispatch_group_notify(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)4-%@", [NSThread currentThread]);
}
});
多線程安全隱患

多線程安全隱患示例01-存錢(qián)取錢(qián)

存錢(qián)取錢(qián)代碼演示
@property (assign, nonatomic) int money;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self moneyTest];
}
/**
存錢(qián)、取錢(qián)演示
*/
- (void)moneyTest
{
self.money = 100;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self drawMoney];
}
});
}
/**
存錢(qián)
*/
- (void)saveMoney
{
int oldMoney = self.money;
sleep(.2);
oldMoney += 50;
self.money = oldMoney;
NSLog(@"存50,還剩%d元 - %@", oldMoney, [NSThread currentThread]);
}
/**
取錢(qián)
*/
- (void)drawMoney
{
int oldMoney = self.money;
sleep(.2);
oldMoney -= 20;
self.money = oldMoney;
NSLog(@"取20,還剩%d元 - %@", oldMoney, [NSThread currentThread]);
}
多線程安全隱患示例02-賣票

賣票代碼演示
/**
賣1張票
*/
@property (assign, nonatomic) int ticketsCount;
- (void)saleTicket
{
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"還剩%d張票 - %@", oldTicketsCount, [NSThread currentThread]);
}
/**
賣票演示
*/
- (void)ticketTest
{
self.ticketsCount = 15;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self ticketTest];
}
多線程安全隱患分析

多線程安全隱患解決方案

iOS 中的線程同步方案
- OSSpinLock
- os_unfair_lock
- pthread_mutex
- dispatch_semaphore
- dispatch_queue(DISPATCH_QUEUE_SERIAL)
- NSLock
- NSRecursiveLock
- NSCondition
- NSConditionLock
- @synchronized
OSSpinLock

os_unfair_lock

pthread_mutex 普通鎖

pthread_mutex--遞歸鎖
遞歸鎖:允許同一個(gè)線程對(duì)一把鎖進(jìn)行重復(fù)加鎖

pthread_mutex--條件鎖

NSLock、NSRecursiveLock

NSCodition

NSConditionLock

dispatch_queue

dispatch_semaphore

@synchronized

所有示例代碼Demo
iOS線程同步方案性能比較

GCD、OperationQueue 對(duì)比
GCD的核心概念:將 任務(wù)(block) 添加到隊(duì)列,并且指定執(zhí)行任務(wù)的函數(shù)。
NSOperation 的核心概念:把 操作(異步) 添加到 隊(duì)列。
區(qū)別
GCD:
將任務(wù)(block)添加到隊(duì)列(串行/并發(fā)/主隊(duì)列),并且指定任務(wù)執(zhí)行的函數(shù)(同步/異步)
GCD是底層的C語(yǔ)言構(gòu)成的API
iOS 4.0 推出的,針對(duì)多核處理器的并發(fā)技術(shù)
在隊(duì)列中執(zhí)行的是由 block 構(gòu)成的任務(wù),這是一個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu)
要停止已經(jīng)加入 queue 的 block 需要寫(xiě)復(fù)雜的代碼
需要通過(guò) Barrier(dispatch_barrier_async)或者同步任務(wù)設(shè)置任務(wù)之間的依賴關(guān)系
只能設(shè)置隊(duì)列的優(yōu)先級(jí)
高級(jí)功能: dispatch_once_t(一次性執(zhí)行, 多線程安全); dispatch_after(延遲); dispatch_group(調(diào)度組); dispatch_semaphore(信號(hào)量); dispatch_apply(優(yōu)化順序不敏感大體量for循環(huán));
OperationQueue:
OC 框架,更加面向?qū)ο?,是?duì) GCD 的封裝。
iOS 2.0 推出的,蘋(píng)果推出 GCD 之后,對(duì) NSOperation 的底層進(jìn)行了全部重寫(xiě)。
可以設(shè)置隊(duì)列中每一個(gè)操作的 QOS() 隊(duì)列的整體 QOS
操作相關(guān) Operation作為一個(gè)對(duì)象,為我們提供了更多的選擇: 任務(wù)依賴(addDependency),可以跨隊(duì)列設(shè)置操作的依賴關(guān)系; 在隊(duì)列中的優(yōu)先級(jí)(queuePriority) 服務(wù)質(zhì)量(qualityOfService, iOS8+); 完成回調(diào)(void (^completionBlock)(void)
隊(duì)列相關(guān) 服務(wù)質(zhì)量(qualityOfService, iOS8+); 最大并發(fā)操作數(shù)(maxConcurrentOperationCount),GCD 不易實(shí)現(xiàn); 暫停/繼續(xù)(suspended); 取消所有操作(cancelAllOperations); KVO 監(jiān)聽(tīng)隊(duì)列任務(wù)執(zhí)行進(jìn)度(progress, iOS13+);
自旋鎖跟互斥鎖比較

atomic

/*
nonatomic和atomic
atom:原子,不可再分割的單位
atomic:原子性
給屬性加上atomic修飾,可以保證屬性的setter和getter都是原子性操作,也就是保證setter和gette內(nèi)部是線程同步的
atomic跟nonatomic 在取值跟設(shè)值的時(shí)候不同,atomic取值設(shè)值會(huì)加鎖(自旋鎖)
atomic經(jīng)常在Mac使用,太耗內(nèi)存
*/

iOS中的讀寫(xiě)安全方案

pthread_rwlock:讀寫(xiě)鎖

dispatch_barrier_async:異步柵欄調(diào)用
