我只是一個(gè)搬運(yùn)工萎胰,僅僅為了加深記憶,感謝作者分享棚辽,文章大部分來(lái)源:尚大大o_O
線程和進(jìn)程
幾乎所有的操作系統(tǒng)都支持同時(shí)運(yùn)行多個(gè)任務(wù)技竟,一個(gè)任務(wù)通常就是一個(gè)程序,每個(gè)程序就是一個(gè)進(jìn)程屈藐。當(dāng)一個(gè)程序運(yùn)行時(shí)榔组,內(nèi)部可能包含了多個(gè)順序執(zhí)行流,每個(gè)順序執(zhí)行流就是一個(gè)線程联逻。
- 進(jìn)程(Process )
當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行后搓扯,即變成一個(gè)進(jìn)程。進(jìn)程是處于是處于運(yùn)行過(guò)程中的程序包归,并且具有一定的獨(dú)立功能锨推,進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。一般而言公壤,進(jìn)程有如下特征:
- 獨(dú)立性:有自己獨(dú)立的資源换可,且擁有自己私有的地址空間。在沒(méi)有經(jīng)過(guò)進(jìn)程本省的允許下厦幅,其他進(jìn)程是不能直接訪問(wèn)其進(jìn)程的地址空間的沾鳄。
- 動(dòng)態(tài)性:程序只是靜態(tài)的指令集合,而進(jìn)程是一個(gè)正在系統(tǒng)中活動(dòng)的指令集合确憨。進(jìn)程有時(shí)間的概念译荞,具有自己的生命周期和各種狀態(tài)。
- 并發(fā)性:多個(gè)進(jìn)程可以在單個(gè)處理器上并發(fā)執(zhí)行休弃,互相不會(huì)影響吞歼。
- 線程(Thread)
線程也被稱做輕量級(jí)進(jìn)程,線程是進(jìn)程的執(zhí)行單元玫芦。就像進(jìn)程在系統(tǒng)中一樣浆熔,線程在進(jìn)程中也是獨(dú)立的,并發(fā)的執(zhí)行流程。一個(gè)進(jìn)程可以擁有多個(gè)線程医增,一個(gè)線程必須有一個(gè)父進(jìn)程慎皱,但不再擁有系統(tǒng)資源,而是和父進(jìn)程一起共享父進(jìn)程的全部資源叶骨。多線程由于共享父進(jìn)程的資源茫多,所以編程更加方便,但是也需要小心線程不會(huì)影響到父進(jìn)程中的其他線程忽刽。線程是獨(dú)立運(yùn)行的天揖,它并不知道其他線程的存在。線程執(zhí)行是搶占式的跪帝,也就是說(shuō)今膊,當(dāng)前運(yùn)行的線程在任何時(shí)候都可能被掛起,以便林另外一個(gè)線程可以運(yùn)行伞剑。
- 多線程優(yōu)點(diǎn)
- 進(jìn)程間不可以共享內(nèi)存斑唬,但線程之間共享內(nèi)存十分容易。
- 系統(tǒng)創(chuàng)建進(jìn)程需要為其重新分配系統(tǒng)資源黎泣,但是創(chuàng)建線程代價(jià)小得多恕刘,因此效率更高
為什么要用多線程編程
為了提高資源利用率來(lái)提升系統(tǒng)整體效率,實(shí)際往往是將耗時(shí)操作放在后臺(tái)執(zhí)行抒倚,避免阻塞主線程褐着,在iOS中UI繪制和用戶響應(yīng)都是主線程。
NSThread
常用API
- (void)viewDidLoad {
[super viewDidLoad];
//打印當(dāng)前線程
NSLog(@"開(kāi)始:%@ 優(yōu)先級(jí):%d", [NSThread currentThread], [NSThread currentThread].qualityOfService);
//1.創(chuàng)建NSTread對(duì)象托呕,必須調(diào)用start方法開(kāi)始含蓉,并且只能傳一個(gè)參數(shù)object
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"test"];
// NSThread *thread = [[NSThread alloc] initWithBlock:^{}];
thread.name = @"testThread";
thread.qualityOfService = NSQualityOfServiceUserInteractive;
[thread start];
//2.直接創(chuàng)建并啟動(dòng)線程
// [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"test"];
// [NSThread detachNewThreadWithBlock:^{}];
//3.隱式直接創(chuàng)建
// [NSThread performSelectorInBackground:@selector(run:) withObject:nil];
// NSLog(@"結(jié)束:%@", [NSThread currentThread]);
}
- (void)run:(NSObject *)object {
//阻塞休眠
// [NSThread sleepForTimeInterval:5];
//中止當(dāng)前線程
// [NSThread exit];
NSLog(@"子線程運(yùn)行:%@ %@ 優(yōu)先級(jí):%d", [NSThread currentThread], object, [NSThread currentThread].qualityOfService);
}
復(fù)制代碼
- 線程的狀態(tài)
線程被啟動(dòng)后,并不是直接進(jìn)入執(zhí)行狀態(tài)项郊,也不是一直處于執(zhí)行狀態(tài)谴餐,由于線程并發(fā),線程會(huì)反復(fù)在運(yùn)行呆抑、就緒間切換。創(chuàng)建一個(gè)線程后汁展,處于新建狀態(tài)鹊碍,系統(tǒng)為其分配內(nèi)存,初始化成員變量食绿;調(diào)用-(void)start侈咕;
方法后,該線程處于就緒狀態(tài)器紧,系統(tǒng)為其創(chuàng)建方法調(diào)用棧和程序計(jì)數(shù)器耀销,此時(shí)并沒(méi)有運(yùn)行,何時(shí)運(yùn)行取決于系統(tǒng)調(diào)度铲汪。
[圖片上傳中...(image-ce30cc-1577264887488-0)]
<figcaption></figcaption>
- 終止子線程
每個(gè)線程都有一定的優(yōu)先級(jí)熊尉,優(yōu)先級(jí)越高獲得執(zhí)行機(jī)會(huì)越多罐柳。目前通過(guò)qualityOfService
屬性來(lái)設(shè)置,原來(lái)的threadPriority
由于語(yǔ)義不夠清晰狰住,已經(jīng)被廢棄了张吉。
NSQualityOfServiceUserInteractive:最高優(yōu)先級(jí),主要用于提供交互UI的操作催植,比如處理點(diǎn)擊事件肮蛹,繪制圖像到屏幕上
NSQualityOfServiceUserInitiated:次高優(yōu)先級(jí),主要用于執(zhí)行需要立即返回的任務(wù)
NSQualityOfServiceDefault:默認(rèn)優(yōu)先級(jí)创南,當(dāng)沒(méi)有設(shè)置優(yōu)先級(jí)的時(shí)候伦忠,線程默認(rèn)優(yōu)先級(jí)
NSQualityOfServiceUtility:普通優(yōu)先級(jí),主要用于不需要立即返回的任務(wù)
NSQualityOfServiceBackground:后臺(tái)優(yōu)先級(jí)稿辙,用于完全不緊急的任務(wù)
復(fù)制代碼
- 缺點(diǎn)
使用NSThread進(jìn)行多線程編程較復(fù)雜昆码,需要自己控制多線程的同步、并發(fā)邓深,還需要自己控制線程的終止銷毀未桥,稍有不留神容易出現(xiàn)錯(cuò)誤,對(duì)開(kāi)發(fā)者要求較高芥备,一般較少使用冬耿。
NSOperation
iOS還提供了NSOperation與NSOperationQueue來(lái)實(shí)現(xiàn)多線程,是基于GCD更高一層的封裝萌壳,完全面向?qū)ο笠嘞狻5荊CD更簡(jiǎn)單易用、代碼可讀性也更高袱瓮。
NSOperationQueue:負(fù)責(zé)管理系統(tǒng)提交的多個(gè)NSOperation缤骨,底層維護(hù)了一個(gè)線程池。不同于GCD中的調(diào)度隊(duì)列FIFO(先進(jìn)先出)原則尺借。NSOperationQueue對(duì)于添加到隊(duì)列中的操作绊起,首先進(jìn)入準(zhǔn)備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴關(guān)系),然后進(jìn)入就緒狀態(tài)的操作的開(kāi)始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對(duì)的優(yōu)先級(jí)決定(優(yōu)先級(jí)是操作對(duì)象自身的屬性)燎斩。
NSOperation: 代表一個(gè)多線程任務(wù)虱歪。
- 為什么要使用NSOperation、NSOPerationQueue栅表?
- 可以添加完成的代碼塊笋鄙,在操作完成后執(zhí)行。
- 添加操作之間的依賴關(guān)系怪瓶,方便的控制執(zhí)行順序萧落。
- 設(shè)定操作執(zhí)行的優(yōu)先級(jí)。
- 可以很方便的取消一個(gè)操作的執(zhí)行。
- 使用KVO觀察對(duì)操作執(zhí)行狀態(tài)的更改:isExecuteing找岖、isFinished陨倡、isCancelled。
- 常用API
NSOperationQueue *queue;
//獲取執(zhí)行當(dāng)前NSOperation的NSOperationQueue隊(duì)列
// queue = [NSOperationQueue currentQueue];
//獲取主線程的NSOperationQueue隊(duì)列
// queue = [NSOperationQueue mainQueue];
//自定義隊(duì)列
queue = [[NSOperationQueue alloc] init];
//隊(duì)列名
queue.name = @"testOperationQueue";
//最大并發(fā)操作數(shù)(系統(tǒng)有限制宣增,即使設(shè)置很大玫膀,也會(huì)自動(dòng)調(diào)整)
queue.maxConcurrentOperationCount = 10;
//設(shè)置優(yōu)先級(jí)
queue.qualityOfService = NSQualityOfServiceDefault;
//自定義NSOperation,如果SEL和Block為空爹脾,系統(tǒng)不會(huì)加入到指定隊(duì)列
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOperation");
}];
//添加依賴關(guān)系帖旨,invocationOperation執(zhí)行完后才執(zhí)行blockOperation
[blockOperation addDependency:invocationOperation];
//添加到隊(duì)列中
// [queue addOperation:invocationOperation];
[queue addOperations:@[invocationOperation, blockOperation] waitUntilFinished:NO];
//直接添加代碼塊任務(wù)
[queue addOperationWithBlock:^{
}];
//打印所有的NSOperation
for(int i=0; i<queue.operationCount; i++) {
NSLog(@"隊(duì)列%@的第%d個(gè)NSOperation:%@", queue.name, i, queue.operations[i]);
}
//終止所有NSOperation
// [queue cancelAllOperations];
//執(zhí)行完所有NSOperation才能解除阻塞當(dāng)前線程
// [queue waitUntilAllOperationsAreFinished];
復(fù)制代碼
GCD(Grand Central Dispatch)
- 基本概念
- 隊(duì)列:隊(duì)列負(fù)責(zé)開(kāi)發(fā)者提交的任務(wù),不過(guò)不同任務(wù)的執(zhí)行時(shí)間不一樣灵妨,先處理的任務(wù)不一定先完成解阅。隊(duì)列即可是串行的,也可是并行的泌霍,隊(duì)列底層會(huì)維持一個(gè)線程池來(lái)處理任務(wù)货抄,串行隊(duì)列只需要維護(hù)一個(gè)線程即可,并行隊(duì)列則需要維護(hù)多個(gè)線程朱转。
- 任務(wù):用戶提交給隊(duì)列的工作單元蟹地,這些任務(wù)將會(huì)提交給隊(duì)列底層維護(hù)的線程池。
- 異步:可以在新的線程中執(zhí)行任務(wù)藤为,但不一定會(huì)開(kāi)辟新的線程怪与。dispatch函數(shù)會(huì)立即返回,然后Block在后臺(tái)異步執(zhí)行缅疟。
- 同步:在當(dāng)前線程執(zhí)行任務(wù)分别,不會(huì)開(kāi)辟新的線程。必須等到Block函數(shù)執(zhí)行完畢后存淫,dispatch函數(shù)才會(huì)返回耘斩。
注:隊(duì)列的串行和并行決定了任務(wù)以何種方式執(zhí)行,執(zhí)行的異步和同步?jīng)Q定了是否需要開(kāi)辟新線程處理任務(wù)桅咆。
- 特點(diǎn)
- GCD可用于多核的并行運(yùn)算括授;
- GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)岩饼;
- GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程刽脖、調(diào)度任務(wù)、銷毀線程)忌愚;
- 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要寫(xiě)任何線程管理代碼却邓;
- 常用API
/** 獲取隊(duì)列 */
//獲取指定優(yōu)先級(jí)的全局并發(fā)隊(duì)列(flag填0即可硕糊,僅預(yù)留的參數(shù),使用其他值可能會(huì)返回null)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建自定義并行隊(duì)列
dispatch_queue_t queue1 = dispatch_queue_create("testQueue1", DISPATCH_QUEUE_CONCURRENT);
//獲取系統(tǒng)主線程關(guān)聯(lián)的串行隊(duì)列
dispatch_queue_t queue2 = dispatch_get_main_queue();
//創(chuàng)建自定義串行隊(duì)列
dispatch_queue_t queue3 = dispatch_queue_create("testQueue3", DISPATCH_QUEUE_SERIAL);
/** 提交任務(wù) */
//異步提交代碼塊到并發(fā)隊(duì)列
dispatch_async(queue, ^{
});
//同步提交代碼塊到自定義并發(fā)隊(duì)列
dispatch_sync(queue1, ^{
});
//異步提交代碼塊到串行隊(duì)列,線程池將在指定時(shí)間執(zhí)行代碼塊(實(shí)際是5秒后加入到隊(duì)列中简十,實(shí)際并不一定會(huì)立馬執(zhí)行檬某,一般精度要求下是沒(méi)問(wèn)題的)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), queue2, ^{
});
//異步提交代碼到自定義串行隊(duì)列,同步函數(shù)螟蝙,無(wú)論是在串行還是并行隊(duì)列中執(zhí)行恢恼,都要執(zhí)行完才返回,所以要防止線程阻塞和死鎖胰默,time表示當(dāng)前是第幾次(如果提交給并發(fā)隊(duì)列场斑,會(huì)啟動(dòng)五個(gè)線程來(lái)執(zhí)行)
dispatch_apply(5, queue3, ^(size_t time) {
});
//實(shí)際是個(gè)long類型變量,用于判斷該代碼塊是否被執(zhí)行過(guò)
static dispatch_once_t onceToken;
//主線程執(zhí)行一次代碼塊
dispatch_once(&onceToken, ^{
});
//等group執(zhí)行完后牵署,才能執(zhí)行下一步
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
/** 組(用于需要等待多個(gè)任務(wù)全部執(zhí)行完再進(jìn)行下一步) */
dispatch_group_t group = dispatch_group_create();
//并發(fā)執(zhí)行的代碼塊1
dispatch_group_async(group, queue, ^{
});
//并發(fā)執(zhí)行的代碼塊2
dispatch_group_async(group, queue, ^{
});
//等待兩個(gè)代碼塊執(zhí)行完匯總
dispatch_group_notify(group, queue, ^{
});
/** 柵欄(用于需要依次執(zhí)行完多個(gè)線程組) */
//并發(fā)隊(duì)列異步執(zhí)行代碼塊1漏隐,2
dispatch_async(queue, ^{
//代碼塊1
});
dispatch_async(queue, ^{
//代碼塊2
});
//1,2執(zhí)行完后才會(huì)執(zhí)行3奴迅,4
dispatch_barrier_async(queue, ^{
});
//并發(fā)隊(duì)列異步執(zhí)行代碼塊3青责,4
dispatch_async(queue, ^{
//代碼塊3
});
dispatch_async(queue, ^{
//代碼塊4
});
/** 信號(hào)量(用于控制線程的等待和執(zhí)行) */
//創(chuàng)建信號(hào)量,value表示初始信號(hào)總量取具,支持多少個(gè)操作來(lái)執(zhí)行
dispatch_semaphore_t t = dispatch_semaphore_create(1);
//發(fā)送一個(gè)信號(hào)脖隶,讓信號(hào)總量+1
dispatch_semaphore_signal(t);
//使信號(hào)總量-1,如果總量為0暇检,則會(huì)一直等待(阻塞所在線程)产阱,直到總量大于0則繼續(xù)執(zhí)行
dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);
/*1.可以將異步執(zhí)行變?yōu)橥綀?zhí)行,如需要等待下載完后再直接返回?cái)?shù)據(jù)(我們也可以通過(guò)block回調(diào))*/
//總信號(hào)量設(shè)置為0
dispatch_semaphore_t t1 = dispatch_semaphore_create(0);
//執(zhí)行耗時(shí)代碼
void (^downloadTask)(void) = ^ {
//下載圖片
...
...
//完成后發(fā)送信號(hào)量
dispatch_semaphore_signal(t1);
};
downloadTask();
//一直等到信號(hào)量計(jì)數(shù)為1才執(zhí)行下一步占哟,也就是等到圖片下載完后
dispatch_semaphore_wait(t1, DISPATCH_TIME_FOREVER);
/*2.保證線程安全*/
//設(shè)置信號(hào)量初始計(jì)數(shù)為1心墅,保證只能有一個(gè)操作能進(jìn)來(lái)
dispatch_semaphore_t t2 = dispatch_semaphore_create(1);
//相當(dāng)于加鎖,消耗使用計(jì)數(shù)榨乎,如果已經(jīng)被一個(gè)線程使用怎燥,后續(xù)只能掛起等待信號(hào)量回復(fù)
dispatch_semaphore_wait(t2, DISPATCH_TIME_FOREVER);
//執(zhí)行業(yè)務(wù)代碼
...
...
//解鎖
dispatch_semaphore_signal(t2);
/*3.模擬NSOperationQueue的最大并發(fā)操作數(shù)*/
//最大并發(fā)操作支持10
dispatch_semaphore_t t3 = dispatch_semaphore_create(10);
//剩余操作同上,其實(shí)就是類似于將NSOperationQueue的maxConcurrentOperationCount設(shè)置為10
復(fù)制代碼
- 后臺(tái)運(yùn)行
在App程序進(jìn)入后臺(tái)時(shí)蜜暑,我們應(yīng)該盡量釋放內(nèi)存和保存用戶數(shù)據(jù)或者狀態(tài)信息铐姚。在默認(rèn)情況下,應(yīng)該僅在5秒鐘處理這些工作肛捍,我們可以通過(guò)UIApplication
的beginBackgroundTaskWithExpirationHandler
方法來(lái)申請(qǐng)延長(zhǎng)處理時(shí)間隐绵,最多有十分鐘。
- (void)applicationDidEnterBackground:(UIApplication *)application {
//聲明關(guān)閉后臺(tái)任務(wù)代碼塊
void (^endBackgroundTask)(UIBackgroundTaskIdentifier backgroudTask) = ^(UIBackgroundTaskIdentifier backgroudTask) {
[[UIApplication sharedApplication] endBackgroundTask:backgroudTask];
backgroudTask = UIBackgroundTaskInvalid;
};
//開(kāi)啟后臺(tái)任務(wù)
__block UIBackgroundTaskIdentifier backgroudTask;
backgroudTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
//十分鐘內(nèi)仍然沒(méi)有完成拙毫,系統(tǒng)處理終止句柄
endBackgroundTask(backgroudTask);
}];
//執(zhí)行相關(guān)代碼
//結(jié)束后臺(tái)任務(wù)
endBackgroundTask(backgroudTask);
}
復(fù)制代碼
- 線程死鎖
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"%@", [NSThread currentThread]);
});
}
復(fù)制代碼
在主隊(duì)列中增加同步代碼塊依许,就會(huì)造成死鎖,由于同步是需要立即順序執(zhí)行的缀蹄,上述代碼中峭跳,Block中的方法需要在viewDidLoad
結(jié)束后才能完成膘婶,但是viewDidLoad
想要結(jié)束又必須先結(jié)束B(niǎo)lock中的方法,所以相互永久等待蛀醉,造成了死鎖悬襟。
GCD會(huì)造成循環(huán)引用嗎?
直接使用GCD的相關(guān)API一般是不會(huì)的拯刁,block結(jié)束后沒(méi)有循環(huán)引用的條件脊岳,YYKit的issues下有個(gè)有去的討論:dispatch_async的block里面需要_weak self嗎?
- 注意
- 同步執(zhí)行會(huì)在當(dāng)前線程執(zhí)行任務(wù)垛玻,不具有開(kāi)辟線程的能力或者說(shuō)沒(méi)有必要開(kāi)辟新的線程割捅。并且,同步執(zhí)行必須等到Block函數(shù)執(zhí)行完畢夭谤,dispatch函數(shù)才會(huì)返回棺牧,從而阻塞同一串行隊(duì)列中外部方法的執(zhí)行。
- 異步執(zhí)行dispatch函數(shù)會(huì)直接返回朗儒,只有異步執(zhí)行才有開(kāi)辟新線程的必要颊乘,但是異步執(zhí)行不一定會(huì)開(kāi)辟新線程。
- 想要開(kāi)辟新線程必須讓任務(wù)在異步執(zhí)行醉锄,想要開(kāi)辟多個(gè)線程乏悄,只有讓任務(wù)在并行隊(duì)列中異步執(zhí)行才可以。執(zhí)行方式和隊(duì)列類型多層組合在一定程度上能夠?qū)崿F(xiàn)對(duì)于代碼執(zhí)行順序的調(diào)度恳不。
- 同步+串行:未開(kāi)辟新線程檩小,串行執(zhí)行任務(wù);同步+并行:未開(kāi)辟新線程烟勋,串行執(zhí)行任務(wù)规求;異步+串行:新開(kāi)辟一條線程,串行執(zhí)行任務(wù)卵惦;異步+并行:開(kāi)辟多條新線程阻肿,并行執(zhí)行任務(wù);在主線程中同步使用主隊(duì)列執(zhí)行任務(wù)沮尿,會(huì)造成死鎖丛塌。
線程安全
線程安全主要是由于系統(tǒng)的線程調(diào)度具有一定的隨機(jī)性造成的,由于是多并發(fā)畜疾,多個(gè)線程同時(shí)對(duì)一份數(shù)據(jù)進(jìn)行讀寫(xiě)赴邻,就可能在讀取執(zhí)行一般的時(shí)候另外一個(gè)線程去寫(xiě)入,導(dǎo)致數(shù)據(jù)異常啡捶。線程安全即保證線程同步
- 線程安全的類的特征
- 該類的對(duì)象可以被多個(gè)線程安全訪問(wèn)姥敛。
- 每個(gè)線程調(diào)用對(duì)象的任意方法都會(huì)得到正確的結(jié)果。
- 每個(gè)線程調(diào)用對(duì)象的任意方法之后瞎暑,該對(duì)象仍保持合理狀態(tài)彤敛。
- @synchronized是對(duì)mutex遞歸鎖的封裝
為了解決這個(gè)問(wèn)題忿偷,Objective-C的多線程支持引入同步,使@synchronized
修飾代碼塊臊泌,被修飾的代碼塊可簡(jiǎn)稱為同步代碼塊,語(yǔ)法格式如下
@synchronized (obj) {
//同步代碼塊
}
復(fù)制代碼
其中obj
就是同步監(jiān)視器揍拆,當(dāng)一個(gè)線程執(zhí)行同步前渠概,必須先獲得同步監(jiān)視器的鎖定,任何時(shí)刻只能有一個(gè)線程獲得鎖定嫂拴,執(zhí)行完成后播揪,才會(huì)釋放,如果此時(shí)有新的線程訪問(wèn)筒狠,那么新線程會(huì)進(jìn)入休眠狀態(tài)猪狈。通常推薦使用可能被并發(fā)訪問(wèn)的共享資源作為同步監(jiān)視器。
iOS中的鎖
1. OSSpinLock(自旋鎖)
- 等待鎖的線程處于忙等(busy-wait)狀態(tài)辩恼,一直占用著CPU資源雇庙;
- 目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)翻轉(zhuǎn)問(wèn)題灶伊;
- 如果等待鎖的線程優(yōu)先級(jí)較高疆前,它會(huì)一直占用著CPU資源,優(yōu)先級(jí)低的線程就無(wú)法釋放鎖聘萨;
- 需要導(dǎo)入頭文件
#import <libkern/OSatomic.h>
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//嘗試加鎖(如果需要等待就不加鎖竹椒,直接返回false;如果不需要等待加鎖米辐,返回true)
bool resule = OSSpinLockTry(&lock);
//加鎖
OSSpinLock(&lock);
//解鎖
OSSpinLockUnlock(&lock);
復(fù)制代碼
2. os_unfair_lock
- 用于取代不安全的OSSpinLock胸完,從iOS10開(kāi)始支持;
- 從底層調(diào)用看翘贮,等待os_unfair_locks鎖的線程會(huì)處于休眠狀態(tài)赊窥,并非忙等;
- 需要導(dǎo)入頭文件
#import <os/lock.h>
//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//嘗試加鎖
os_unfair_lock_trylock(&lock);
//加鎖
os_unfair_lock_lock(&lock);
//解鎖
os_unfair_lock_unlock(&lock);
復(fù)制代碼
3. pthread_mutex
互斥鎖
- mutex叫做“互斥鎖”择膝,等待的線程會(huì)處于休眠狀態(tài)
- 需要導(dǎo)入頭文件
#import <pthread.h>
//初始化鎖的屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);
//初始化
pthread_mutex_t mutex;
pthread_mutex_init (&mutex,&attr);
//嘗試加鎖
pthread_mutex_trylock (&mutex);
//加鎖
pthread_mutex_lock (&mutex);
//解鎖
pthread_mutex_unlock (&mutex);
//銷毀相關(guān)資源
pthread_mutexattr_unlock(&attr)誓琼;
pthread_mutex_destroy(&mutex);
復(fù)制代碼
遞歸鎖
- 遞歸鎖:允許同一個(gè)線程對(duì)一把鎖進(jìn)行重復(fù)加鎖
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
// 初始化鎖
pthread_mutex_t mutex;
pthread_mutex_init(mutex, &attr);
// 銷毀屬性
pthread_mutexattr_destroy(&attr);
復(fù)制代碼
條件
// 初始化鎖
pthread_mutex_t mutex;
//NULL代表使用默認(rèn)屬性
pthread_mutex_init(&mutex, NULL);
// 初始化條件
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
//等待條件(進(jìn)入休眠肴捉,放開(kāi)mutex鎖腹侣;被喚醒后,會(huì)再次對(duì)mutex加鎖)
pthread_cond_wait(&cond, &mutex);
//激活一個(gè)等待條件的線程
pthread_cond_signal(&cond);
//銷毀資源
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
復(fù)制代碼
4. NSLock齿穗、NSRecursiveLock
- NSLock是對(duì)mutex普通鎖的封裝
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking>
{
- (BOOL)tryLock;
- (BOOl)lockBeforeDate:(NSDate *)limit;
}
@end
//初始化鎖
NSLock *lock = [[NSLock alloc] init];
復(fù)制代碼
- NSRecursiveLock也是對(duì)mutex遞歸所得封裝傲隶,API跟NSLock基本一致。
5. NSCondition
- NScondition 是對(duì)mutex和cond的封裝
@interface NSCondition : NSObject <NSLocking>
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
復(fù)制代碼
6. NSConditionLock
- NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝窃页,可以設(shè)置具體的條件值
@interface NSConditionLock : NSObject <NSLocking> {
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
}
@end
復(fù)制代碼
7. dispatch_semaphore
- semaphore叫做信號(hào)量跺株;
- 信號(hào)量的初始值复濒,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量;
- 信號(hào)量的初始值為1乒省,代表同時(shí)只允許1條線程訪問(wèn)資源巧颈,保證線程同步
//信號(hào)量的初始值
int value = 1;
//初始化信號(hào)量
dispatch_semaphore semephore = dispatch_semaphore_creat(value);
//如果信號(hào)量的值<=0袖扛,當(dāng)前線程就會(huì)進(jìn)入休眠等待(直到信號(hào)量的值>0)
//如果信號(hào)量的值>0, 就減1跷睦,然后往下執(zhí)行后面的代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//讓信號(hào)量的值加1
dispatch_semaphore_signal(semaphore)铅搓;
復(fù)制代碼
8. dispatch_queue
- 直接使用GCD的串行隊(duì)列,也是可以實(shí)現(xiàn)線程同步的
dispatch_queue_t queue = dispatch_queue_creat("lock_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//任務(wù)
})