iOS多線程知識(shí)總結(jié)

我只是一個(gè)搬運(yùn)工,僅僅為了加深記憶,感謝作者分享,文章大部分來源:尚大大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)行過程中的程序,并且具有一定的獨(dú)立功能,進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。一般而言,進(jìn)程有如下特征:

  1. 獨(dú)立性:有自己獨(dú)立的資源,且擁有自己私有的地址空間。在沒有經(jīng)過進(jìn)程本省的允許下,其他進(jìn)程是不能直接訪問其進(jìn)程的地址空間的。
  2. 動(dòng)態(tài)性:程序只是靜態(tài)的指令集合,而進(jìn)程是一個(gè)正在系統(tǒng)中活動(dòng)的指令集合。進(jìn)程有時(shí)間的概念,具有自己的生命周期和各種狀態(tài)。
  3. 并發(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í)行是搶占式的,也就是說,當(dāng)前運(yùn)行的線程在任何時(shí)候都可能被掛起,以便林另外一個(gè)線程可以運(yùn)行。

  • 多線程優(yōu)點(diǎn)
  1. 進(jìn)程間不可以共享內(nèi)存,但線程之間共享內(nèi)存十分容易。
  2. 系統(tǒng)創(chuàng)建進(jìn)程需要為其重新分配系統(tǒng)資源,但是創(chuàng)建線程代價(jià)小得多,因此效率更高

為什么要用多線程編程


為了提高資源利用率來提升系統(tǒng)整體效率,實(shí)際往往是將耗時(shí)操作放在后臺(tái)執(zhí)行,避免阻塞主線程,在iOS中UI繪制和用戶響應(yīng)都是主線程。

NSThread


常用API

- (void)viewDidLoad {
    [super viewDidLoad];

    //打印當(dāng)前線程
    NSLog(@"開始:%@   優(yōu)先級(jí):%d", [NSThread currentThread], [NSThread currentThread].qualityOfService);

    //1.創(chuàng)建NSTread對(duì)象,必須調(diào)用start方法開始,并且只能傳一個(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í)并沒有運(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ì)越多。目前通過qualityOfService屬性來設(shè)置,原來的threadPriority由于語義不夠清晰,已經(jīng)被廢棄了。

NSQualityOfServiceUserInteractive:最高優(yōu)先級(jí),主要用于提供交互UI的操作,比如處理點(diǎn)擊事件,繪制圖像到屏幕上
NSQualityOfServiceUserInitiated:次高優(yōu)先級(jí),主要用于執(zhí)行需要立即返回的任務(wù)
NSQualityOfServiceDefault:默認(rèn)優(yōu)先級(jí),當(dāng)沒有設(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ì)開發(fā)者要求較高,一般較少使用。

NSOperation


iOS還提供了NSOperation與NSOperationQueue來實(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)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對(duì)的優(yōu)先級(jí)決定(優(yōu)先級(jí)是操作對(duì)象自身的屬性)。

NSOperation: 代表一個(gè)多線程任務(wù)。

  • 為什么要使用NSOperation、NSOPerationQueue?
  1. 可以添加完成的代碼塊,在操作完成后執(zhí)行。
  2. 添加操作之間的依賴關(guān)系,方便的控制執(zhí)行順序。
  3. 設(shè)定操作執(zhí)行的優(yōu)先級(jí)。
  4. 可以很方便的取消一個(gè)操作的執(zhí)行。
  5. 使用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)

  • 基本概念
  1. 隊(duì)列:隊(duì)列負(fù)責(zé)開發(fā)者提交的任務(wù),不過不同任務(wù)的執(zhí)行時(shí)間不一樣,先處理的任務(wù)不一定先完成。隊(duì)列即可是串行的,也可是并行的,隊(duì)列底層會(huì)維持一個(gè)線程池來處理任務(wù),串行隊(duì)列只需要維護(hù)一個(gè)線程即可,并行隊(duì)列則需要維護(hù)多個(gè)線程。
  2. 任務(wù):用戶提交給隊(duì)列的工作單元,這些任務(wù)將會(huì)提交給隊(duì)列底層維護(hù)的線程池。
  3. 異步:可以在新的線程中執(zhí)行任務(wù),但不一定會(huì)開辟新的線程。dispatch函數(shù)會(huì)立即返回,然后Block在后臺(tái)異步執(zhí)行。
  4. 同步:在當(dāng)前線程執(zhí)行任務(wù),不會(huì)開辟新的線程。必須等到Block函數(shù)執(zhí)行完畢后,dispatch函數(shù)才會(huì)返回。

注:隊(duì)列的串行和并行決定了任務(wù)以何種方式執(zhí)行,執(zhí)行的異步和同步?jīng)Q定了是否需要開辟新線程處理任務(wù)。

  • 特點(diǎn)
  1. GCD可用于多核的并行運(yùn)算;
  2. GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核);
  3. GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程);
  4. 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要寫任何線程管理代碼;
  • 常用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í)行,一般精度要求下是沒問題的)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), queue2, ^{

    });

    //異步提交代碼到自定義串行隊(duì)列,同步函數(shù),無論是在串行還是并行隊(duì)列中執(zhí)行,都要執(zhí)行完才返回,所以要防止線程阻塞和死鎖,time表示當(dāng)前是第幾次(如果提交給并發(fā)隊(duì)列,會(huì)啟動(dòng)五個(gè)線程來執(zhí)行)
    dispatch_apply(5, queue3, ^(size_t time) {

    });

    //實(shí)際是個(gè)long類型變量,用于判斷該代碼塊是否被執(zhí)行過
    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è)操作來執(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ù)(我們也可以通過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)來
    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秒鐘處理這些工作,我們可以通過UIApplicationbeginBackgroundTaskWithExpirationHandler方法來申請(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;
    };

    //開啟后臺(tái)任務(wù)
    __block UIBackgroundTaskIdentifier backgroudTask;
    backgroudTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        //十分鐘內(nè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é)束Block中的方法,所以相互永久等待,造成了死鎖。

GCD會(huì)造成循環(huán)引用嗎?

直接使用GCD的相關(guān)API一般是不會(huì)的,block結(jié)束后沒有循環(huán)引用的條件,YYKit的issues下有個(gè)有去的討論:dispatch_async的block里面需要_weak self嗎?

  • 注意
  1. 同步執(zhí)行會(huì)在當(dāng)前線程執(zhí)行任務(wù),不具有開辟線程的能力或者說沒有必要開辟新的線程。并且,同步執(zhí)行必須等到Block函數(shù)執(zhí)行完畢,dispatch函數(shù)才會(huì)返回,從而阻塞同一串行隊(duì)列中外部方法的執(zhí)行。
  2. 異步執(zhí)行dispatch函數(shù)會(huì)直接返回,只有異步執(zhí)行才有開辟新線程的必要,但是異步執(zhí)行不一定會(huì)開辟新線程。
  3. 想要開辟新線程必須讓任務(wù)在異步執(zhí)行,想要開辟多個(gè)線程,只有讓任務(wù)在并行隊(duì)列中異步執(zhí)行才可以。執(zhí)行方式和隊(duì)列類型多層組合在一定程度上能夠?qū)崿F(xiàn)對(duì)于代碼執(zhí)行順序的調(diào)度。
  4. 同步+串行:未開辟新線程,串行執(zhí)行任務(wù);同步+并行:未開辟新線程,串行執(zhí)行任務(wù);異步+串行:新開辟一條線程,串行執(zhí)行任務(wù);異步+并行:開辟多條新線程,并行執(zhí)行任務(wù);在主線程中同步使用主隊(duì)列執(zhí)行任務(wù),會(huì)造成死鎖。

線程安全

線程安全主要是由于系統(tǒng)的線程調(diào)度具有一定的隨機(jī)性造成的,由于是多并發(fā),多個(gè)線程同時(shí)對(duì)一份數(shù)據(jù)進(jìn)行讀寫,就可能在讀取執(zhí)行一般的時(shí)候另外一個(gè)線程去寫入,導(dǎo)致數(shù)據(jù)異常。線程安全即保證線程同步

  • 線程安全的類的特征
  1. 該類的對(duì)象可以被多個(gè)線程安全訪問。
  2. 每個(gè)線程調(diào)用對(duì)象的任意方法都會(huì)得到正確的結(jié)果。
  3. 每個(gè)線程調(diào)用對(duì)象的任意方法之后,該對(duì)象仍保持合理狀態(tài)。
  • @synchronized是對(duì)mutex遞歸鎖的封裝

為了解決這個(gè)問題,Objective-C的多線程支持引入同步,使@synchronized修飾代碼塊,被修飾的代碼塊可簡(jiǎn)稱為同步代碼塊,語法格式如下

@synchronized (obj) {
    //同步代碼塊
}
復(fù)制代碼

其中obj就是同步監(jiān)視器,當(dāng)一個(gè)線程執(zhí)行同步前,必須先獲得同步監(jiān)視器的鎖定,任何時(shí)刻只能有一個(gè)線程獲得鎖定,執(zhí)行完成后,才會(huì)釋放,如果此時(shí)有新的線程訪問,那么新線程會(huì)進(jìn)入休眠狀態(tài)。通常推薦使用可能被并發(fā)訪問的共享資源作為同步監(jiān)視器。

iOS中的鎖


1. OSSpinLock(自旋鎖)

  • 等待鎖的線程處于忙等(busy-wait)狀態(tài),一直占用著CPU資源;
  • 目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)翻轉(zhuǎn)問題;
  • 如果等待鎖的線程優(yōu)先級(jí)較高,它會(huì)一直占用著CPU資源,優(yōu)先級(jí)低的線程就無法釋放鎖;
  • 需要導(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開始支持;
  • 從底層調(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)入休眠,放開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)量的初始值,可以用來控制線程并發(fā)訪問的最大數(shù)量;
  • 信號(hào)量的初始值為1,代表同時(shí)只允許1條線程訪問資源,保證線程同步
//信號(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ù)
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容