這些年我們用過的線程

進程

在系統(tǒng)中正在運行的一個應用程序。每個進程之間是獨立的庭惜,每個進程均運行在其專用且受保護的內(nèi)存空間內(nèi)这揣。一個進程至少要有一個線程,線程是進程的基本執(zhí)行單元怀吻,進程的任務都是在線程中完成的瞬浓。

多線程的優(yōu)缺點

  • 優(yōu)點:
    1.適當提高程序執(zhí)行效率;
    2.適當提高資源利用率蓬坡;
  • 缺點:
    1.開啟線程需要占一定的內(nèi)存空間(一般情況下主線程占用1M猿棉,子線程占用512KB),大量開啟線程屑咳,將降低程序的性能萨赁;
    2.線程越多,CPU調(diào)度開銷就越大乔宿;
    3.線程之前的通訊和數(shù)據(jù)共享越復雜位迂;

實現(xiàn)方案

Pthread

1 特點:
(1)一套通用的多線程API
(2)適用于Unix\Linux\Windows等系統(tǒng)
(3)跨平臺\可移植
(4)使用難度大
2 使用語言:c語言
3 使用頻率:幾乎不用
4 線程生命周期:由程序員進行管理

NSThread

1 特點:
(1)使用更加面向?qū)ο?br> (2)簡單易用,可直接操作線程對象
2 使用語言:C語言
3 使用頻率:偶爾使用
4 線程生命周期:由程序員進行管理
5 實現(xiàn)方案:線程的狀態(tài):新建详瑞、就緒掂林、運行、阻塞坝橡、死亡

  • 初始化
//初始化   
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(timeRun:) object:@"123"];   
thread.name = @"YAN";   
// 設置優(yōu)先級:(0.0~1.0)1.0最高優(yōu)先級泻帮,默認為0.5   
thread.threadPriority = 1.0;   
// 開啟線程    [thread start];
// 初始化:馬上創(chuàng)建并開啟新的線程   
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"yan"];
//初始化:隱式創(chuàng)建   
[self performSelectorInBackground:@selector(background:) withObject:@"創(chuàng)建"];
  • 阻塞/暫停
  • 獲取該線程: [NSThread currentThread]
  • 獲取主線程: [NSThread mainThread]
  • 退出該線程: [NSThread exit]; 不能在主線程中調(diào)用該方法;該線程死亡之后線程的代碼將不在執(zhí)行
GCD

1 特點:
(1)旨在替代NSThread等線程技術
(2)充分利用設備的多核(自動)
(3)并發(fā)數(shù)量、執(zhí)行順序是不可控的
(4)只支持隊列的FIFO原則
2 使用語言:OC語言
3 使用頻率:經(jīng)常使用
4 線程生命周期:自動管理
5 底層實現(xiàn):
a.基于XNU內(nèi)核實現(xiàn)
b.屬于libdispatch庫
c.dispatchQueue:管理block计寇,dispatchSource:處理事件
6 實現(xiàn)方案
同步執(zhí)行:不具備開啟新線程的能力锣杂,只能在當前線程執(zhí)行任務;
異步執(zhí)行:具備開啟新線程的能力番宁,在新開啟的線程執(zhí)行任務元莫;
并發(fā)隊列:多個任務同時執(zhí)行;
串行隊列:一個任務完成之后執(zhí)行下一個任務蝶押;

  • 創(chuàng)建 Queues:
     dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr)//隊列名稱踱蠢、隊列屬性(為null時為串行隊列)

①串行隊列:任務添加到隊列中馬上執(zhí)行

     dispatch_queue_t serialQueue = dispatch_queue_create("yan.liu",DISPATCH_QUEUE_SERIAL);
     dispatch_queue_t mainQueue = dispatch_get_main_queue();//主隊列為系統(tǒng)創(chuàng)建的特殊串行隊列,刷新UI在主隊列進行棋电;

②并發(fā)隊列:將所有任務添加到隊列之后才執(zhí)行

dispatch_queue_t concurrentQueue = dispatch_queue_create("yan.liu", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 系統(tǒng)創(chuàng)建的全局并發(fā)隊列
DISPATCH_QUEUE_PRIORITY_HIGH 高優(yōu)先級
DISPATCH_QUEUE_PRIORITY_DEFAULT 默認優(yōu)先級
DISPATCH_QUEUE_PRIORITY_LOW 低優(yōu)先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后臺執(zhí)行
  • 處理事件:

①異步串行:開啟了一個子線程茎截,按照任務的添加順序苇侵,并且任務添加到隊列中馬上執(zhí)行

dispatch_async(serialQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_async(serialQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

②同步串行:沒有開啟新的線程,在當前線程中進行企锌,按照任務的添加順序榆浓,并且任務添加到隊列中馬上執(zhí)行

dispatch_sync(serialQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_sync(serialQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

③異步并行:開啟新的線程,所有任務添加完之后執(zhí)行

dispatch_async(concurrentQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_async(concurrentQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

④同步并行:沒有開啟新的線程撕攒,所有任務添加完成之后之后

dispatch_sync(concurrentQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_sync(concurrentQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

注意:主隊列不能同步執(zhí)行陡鹃,會造成線程卡死

  • 其他方法
    1. 柵欄方法
    NSLog(@"start");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"1-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"2-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"3-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"4-%@",[NSThread currentThread]);
    });
    NSLog(@"end");
運行結(jié)果
  1. 使用dispatch_barrier_async
    NSLog(@"start");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"1-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"2-%@",[NSThread currentThread]);
    });
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"barrier-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"3-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"4-%@",[NSThread currentThread]);
    });
    NSLog(@"end");
運行結(jié)果
  1. 延遲方法:dispatch_after 延遲5S
NSLog(@"start");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"end-%@",[NSThread currentThread]);
    });
運行結(jié)果
  1. 一次性函數(shù):dispatch_once 常用于單例
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"執(zhí)行一次");
    });
  1. 快速迭代方法:與for不同的是:在并行隊列中同時遍歷并且創(chuàng)建新的線程;在串行隊列中和for類似打却,不會創(chuàng)建新的線程
    dispatch_apply(5, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t count) {
        NSLog(@"%zu - %@",count,[NSThread currentThread]);
    });
運行結(jié)果
  1. 組隊列:
dispatch_group_wait(group, DISPATCH_TIME_FOREVER) 等待group執(zhí)行完畢繼續(xù)往下進行杉适,會阻塞線程;
dispatch_group_notify 等待group執(zhí)行完畢執(zhí)行block柳击,不會阻塞進程猿推;
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, concurrentQueue, ^(size_t count) {
            NSLog(@"%zu -1- %@",count,[NSThread currentThread]);
        });
    });
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, serialQueue, ^(size_t count) {
            NSLog(@"%zu -2- %@",count,[NSThread currentThread]);
        });
    });
//    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"回到 - %@",[NSThread currentThread]);
    });
    NSLog(@"1234567");
運行結(jié)果
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, concurrentQueue, ^(size_t count) {
            NSLog(@"%zu -1- %@",count,[NSThread currentThread]);
        });
    });
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, serialQueue, ^(size_t count) {
            NSLog(@"%zu -2- %@",count,[NSThread currentThread]);
        });
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        NSLog(@"回到 - %@",[NSThread currentThread]);
//    });
    NSLog(@"1234567");

運行結(jié)果

注意:dispatch_group_notify不能與 dispatch_async結(jié)合使用,否則會導致group任務沒有執(zhí)行完畢捌肴,就執(zhí)行了dispatch_group_notify

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, globalQueue, ^{
        dispatch_async(concurrentQueue, ^{
            for (int i = 0; i < 3; i ++) {
                NSLog(@"%d -1- %@",i,[NSThread currentThread]);
            }
        });
    });
    dispatch_group_async(group, globalQueue, ^{
        dispatch_async(concurrentQueue, ^{
            for (int i = 0; i < 3; i ++) {
                sleep(1);
                NSLog(@"%d -2- %@",i,[NSThread currentThread]);
            }
        });
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"回到 - %@",[NSThread currentThread]);
    });
    NSLog(@"1234567");
運行結(jié)果

dispatch_group_enter(group); …… dispatch_group_leave(group);中間的任務由group管理蹬叭,dispatch_group_enter(group)group任務加1,dispatch_group_leave(group)group任務減1状知;

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            sleep(1);
            NSLog(@"%d -1- %@",i,[NSThread currentThread]);
        }
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d -2- %@",i,[NSThread currentThread]);
        }
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"回到 - %@",[NSThread currentThread]);
    });
    NSLog(@"123456789");
運行結(jié)果
  1. 信號量:dispatch_semaphore

是一個整形值并且具有一個初始計數(shù)值秽五,并且支持兩個操作:信號通知和等待。當一個信號量被信號通知饥悴,其計數(shù)會被增加坦喘。當一個線程在一個信號量上等待時,線程會被阻塞(如果有必要的話)西设,直至計數(shù)器大于零瓣铣,然后線程會減少這個計數(shù),并繼續(xù)執(zhí)行下面的語句。

①創(chuàng)建信號量: dispatch_semaphore_t sema = dispatch_semaphore_create(0);
②發(fā)送信號: dispatch_semaphore_signal(sema) 信號量加1
③等待信號: dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER) 信號量減1

timeout:dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC) 5秒
DISPATCH_TIME_FOREVER 遙遠的未來
DISPATCH_TIME_NOW 當前時間
在等待時間timeout期間沒有獲得信號量或者信號量一直為0贷揽,其所處的線程將繼續(xù)執(zhí)行下面的語句棠笑;

  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    __block long x = 0;
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1-%ld",x);
        sleep(2);
        x=dispatch_semaphore_signal(sema);
        NSLog(@"2-%ld",x);
    });
    NSLog(@"3-%ld",x);
    x=dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    NSLog(@"4-%ld",x);
運行結(jié)果
  1. dispatch_suspend(queue)掛起queue:不會掛起正在進行的任務,當前任務完成之后掛起之后的任務
  2. dispatch_resume(queue)恢復queue:恢復被掛起的queue
    注意:dispatch_suspend/dispatch_resume對于系統(tǒng)的queue不起作用禽绪;掛起 dispatch_sync中的queue會造成死鎖蓖救;成對出現(xiàn);
  3. dispatch_retain() 印屁、dispatch_release() :ARC中并不需要自己管理
NSOperation

1 特點:
(1)基于GCD(底層是GCD)封裝的API
(2)比GCD多了一些更簡單實用的功能
(3)使用更加面向?qū)ο?br> (4)可以控制并發(fā)數(shù)量循捺,調(diào)整執(zhí)行順序和操作之間的依賴關系
(5)執(zhí)行速度沒有GCD快
2 使用語言:OC語言
3 使用頻率:經(jīng)常使用
4 線程生命周期:自動管理
5 實現(xiàn)方案:

  • NSOperation:默認是同步執(zhí)行
執(zhí)行操作:start
取消操作: cancel
監(jiān)聽操作:設置在start之前
    [invocation setCompletionBlock:^{
        NSLog(@"執(zhí)行完畢");
    }];
    invocation.completionBlock = ^{
        NSLog(@"執(zhí)行完畢");
    };
  • NSInvocationOperation:在主線程中執(zhí)行
    NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocation:) object:@"yan"];
    [invocation start];
    - (void)invocation:(id)obj{
         NSLog(@"%@--%@",obj,[NSThread currentThread]);
    }
  • NSBlockOperation:首先在主線程執(zhí)行,如果主線程被占用就開啟新的線程雄人,多個任務時異步并發(fā)
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
    }];
    [blockOperation start];
  • NSOperationQueue

主隊列: NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
其他隊列: NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init]; 通過設置并發(fā)數(shù)確定隊列是并發(fā)還是串行从橘、異步

addOperationWithBlock:默認異步并發(fā), maxConcurrentOperationCount默認為-1,即并發(fā)洋满;設置為1時,otherQueue為串行隊列珍坊,大于1位并行隊列牺勾;

    otherQueue.maxConcurrentOperationCount = 1;
    [otherQueue addOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
運行結(jié)果

addOperation:
添加NSInvocationOperation:在新線程中執(zhí)行;
添加NSBlockOperation:異步并發(fā)
[otherQueue cancelAllOperations];取消這句代碼之前隊列里添加的所有沒有執(zhí)行的任務阵漏;

    [otherQueue addOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [otherQueue cancelAllOperations];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
運行結(jié)果

[otherQueue setSuspended:YES];YES:暫停驻民;NO:恢復
[invocation setQueuePriority:NSOperationQueuePriorityLow];
[blockOperation4 setQueuePriority:NSOperationQueuePriorityHigh]; 設置queue中的優(yōu)先級,相對于同一個queue履怯;對執(zhí)行順序不起絕對想作用回还;好像并沒有起作用;

添加依賴:可以不同queue之間創(chuàng)建依賴叹洲,但是不能創(chuàng)建環(huán)形依賴;
依賴影響任務的執(zhí)行順序:

// 順序隨機
    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue addOperation:blockOperation3];
    [otherQueue addOperation:blockOperation4];
    [blockOperation1 addDependency:blockOperation4];
    [blockOperation4 addDependency:blockOperation2];

添加依賴后的運行結(jié)果

等待任務完成:等待單個任務完成:waitUntilFinished 阻塞線程(當前線程與主線程)柠硕,直到任務完成;要寫在任務添加到queue之后运提,否則阻塞線程蝗柔;阻塞waitUntilFinished之后的命令;

    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue addOperation:blockOperation3];
    [blockOperation1 waitUntilFinished];
    [otherQueue addOperation:blockOperation4];
    NSLog(@"end--%@",[NSThread currentThread]);
運行結(jié)果

這樣添加不阻塞主線程:

    NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];
    }];
    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        [blockOperation1 waitUntilFinished];
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4--%@",[NSThread currentThread]);
    }];
    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue addOperation:blockOperation3];
    [otherQueue addOperation:blockOperation4];
    NSLog(@"end--%@",[NSThread currentThread]);

運行結(jié)果

等待queue中的任務完成:阻塞線程(當前線程和主線程)民泵,直到queue添加的任務完成癣丧;在waitUntilAllOperationsAreFinished之后添加的任務不算在內(nèi);

    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue waitUntilAllOperationsAreFinished];
    [otherQueue addOperation:blockOperation3];
    [otherQueue addOperation:blockOperation4];
    NSLog(@"end--%@",[NSThread currentThread]);
運行結(jié)果

線程安全

多個線程訪問同一塊資源會產(chǎn)生線程安全問題栈妆;

  • 死鎖:線程A和線程B各持有一把鎖胁编,線程A在等線程B釋放鎖,線程B在等線程A釋放鎖鳞尔;兩個線程都拿不到鎖嬉橙,也不能釋放自己的鎖,造成線程被阻塞在進程中铅檩。
  • 活鎖:線程A可以使用資源憎夷,但是它讓給了其他線程;線程B也可以使用資源昧旨,它也讓給了其他線程先使用拾给;最后導致兩個線程都無法使用資源,導致活鎖兔沃;線程沒有被阻塞蒋得;
  • 解決方案:參考
    • 原子操作:最簡單最基礎的線程安全方法;一個線程對共享變量進行操作時乒疏,其他線程不能對該變量進行操作额衙,并且其他線程不會被阻塞;
    • 內(nèi)存屏障:幫助CPU規(guī)定操作指令的順序,會降低編譯器的性能
    • 可見變量:CPU為了提高處理速度窍侧,通常情況是先將主存中的數(shù)據(jù)讀取到緩存中县踢,進行的一些操作之后也不會立即寫回主存;所以很可能會讀取到舊值伟件;但在共享變量加volatile關鍵字硼啤,聲明為可見變量;CPU從緩存中讀取并進行修改之后會將該變量立即存入到主存中斧账,其他CPU操作時會先判斷緩存中的數(shù)據(jù)是否過期谴返,如果過期會從主存中重新緩存;會降低編譯器的性能咧织;
    • 單例:
    • 信號量(GCD):確保代碼不會被并發(fā)調(diào)用
    • 加鎖保護:阻塞線程嗓袱,性能降低
    1. 互斥鎖:互斥鎖扮演的角色就是代碼或者說任務的柵欄,它將你希望保護的代碼片段圍起來习绢,當其他線程也試圖執(zhí)行這段代碼時會被互斥鎖阻塞渠抹,直到互斥鎖被釋放,如果多個線程同時競爭一個互斥鎖闪萄,有且只有一個線程可以獲得互斥鎖逼肯。
    2. 遞歸鎖:遞歸鎖是互斥鎖的變種。它允許一個線程在已經(jīng)擁有一個鎖桃煎,并且沒有釋放的前提下再次獲得鎖篮幢。當該線程釋放鎖時也需要一個一個釋放。
    3. 讀寫鎖:讀寫鎖一般用在有資源被多個線程頻繁的進行讀操作为迈,而只偶爾會有專職線程對該資源進行寫操作的情況下三椿。讀寫鎖可被多個進行讀操作的線程獲得,但只能被一個進行寫操作的線程獲得葫辐,當有讀操作的線程等待時搜锰,寫操作的線程就不能獲得鎖,反之亦然耿战,當寫操作的線程在等待時蛋叼,讀操作的線程就不能獲得鎖。
    4. 分配鎖:這種鎖作用在進程級別剂陡,將進程保護起來狈涮,但是該鎖不會阻塞其他進程,而是當其他進程與被保護進程交互時分配鎖會告知前來的訪問進程被訪問進程處于鎖狀態(tài)鸭栖,讓前來訪問的進程自行決定下一個操作歌馍。
    5. 自旋鎖:自旋鎖與互斥鎖有點類似,但不同的是其他線程不會被自旋鎖阻塞晕鹊,而是而是在進程中空轉(zhuǎn)松却,就是執(zhí)行一個空的循環(huán)暴浦。一般用于自旋鎖被持有時間較短的情況。
    6. 雙檢測鎖:這種鎖的目的是為了最大限度推遲上鎖的時間晓锻,因為在多線程中線程安全對開銷還是挺大的歌焦,所以一般能不上鎖就不上鎖。所以這種鎖在上鎖之前會先檢查一次是否需要上鎖砚哆,在上鎖之后再檢查一次同规,最后才真正執(zhí)行操作。
    7. Conditions :一種多線程間協(xié)調(diào)通信的機制窟社,用于標明共享資源是否可被訪問或者確保一系列任務能按照指定的執(zhí)行順序執(zhí)行。如果一個線程試圖訪問一個共享資源绪钥,而正在訪問該資源的線程將其條件設置為不可訪問灿里,那么該線程會被阻塞,直到正在訪問該資源的線程將訪問條件更改為可訪問狀態(tài)或者說給被阻塞的線程發(fā)送信號后程腹,被阻塞的線程才能正常訪問這個資源匣吊。

如果使用鎖機制,則不要使用volatile關鍵字寸潦;

  • 實現(xiàn)方案:
  • NSLock
    當一個線程進行訪問的時候色鸳,該線程獲得鎖,其他線程進行訪問的時候见转,將被操作系統(tǒng)掛起命雀,直到該線程釋放鎖,其他線程才能對其進行訪問
    tryLock:當前線程嘗試視圖獲取鎖斩箫,獲取失敗不會阻塞當前線程吏砂;
 NSLock *lock = [[NSLock alloc] init];   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"加鎖%@",[NSThread currentThread]);
        sleep(3);
        NSLog(@"解鎖%@",[NSThread currentThread]);
        [lock unlock];
    });
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock tryLock]) {
            NSLog(@"鎖可用%@",[NSThread currentThread]);
            [lock unlock];
        }else{
            NSLog(@"鎖不可用%@",[NSThread currentThread]);
        }
        NSLog(@"%@",[NSThread currentThread]);
    });
運行結(jié)果

[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4.0]]:在時間限制內(nèi)獲取鎖,時間限制內(nèi)獲取失敗會阻塞線程乘客,直到獲取成功狐血;時間超時后不會阻塞線程;

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4.0]]) {
            NSLog(@"鎖可用%@",[NSThread currentThread]);
            [lock unlock];
        }else{
            NSLog(@"鎖不可用%@",[NSThread currentThread]);
        }
        NSLog(@"%@",[NSThread currentThread]);
    });
運行結(jié)果
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]) {
            NSLog(@"鎖可用%@",[NSThread currentThread]);
            [lock unlock];
        }else{
            NSLog(@"鎖不可用%@",[NSThread currentThread]);
        }
        NSLog(@"%@",[NSThread currentThread]);
    });
運行結(jié)果
  • NSRecursiveLock:可以被同一線程多次請求易核,也不會造成死鎖匈织;主要用于循環(huán)和遞歸
    以下遞歸函數(shù)如果使用NSLock會造成死鎖,而使用NSRecursiveLock就不會
    NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^lockMethod)(int);
        lockMethod = ^(int count){
            [recursiveLock lock];
            if (count < 5) {
                NSLog(@"%d",count);
                lockMethod(count+1);
            }
            [recursiveLock unlock];
            NSLog(@"%d---%@",count,[NSThread currentThread]);
        };
        lockMethod(0);
    });

[recursiveLock tryLock]/ [recursiveLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]:同NSLock

  • NSConditionLock:可以設置加鎖和釋放鎖的條件

[conditionLock lockWhenCondition:0]:condition為0 的時候加鎖牡直;
[conditionLock tryLockWhenCondition:1]:嘗試獲取condition為1的鎖缀匕;不會阻塞線程;
[conditionLock unlockWithCondition:1]:解鎖碰逸,并將condition設置為1弦追;
[conditionLock lockWhenCondition:1 beforeDate:[NSDate dateWithTimeIntervalSinceNow:3]]:在時間限制內(nèi)獲取condition為1的鎖;阻塞當前線程花竞;

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [conditionLock lockWhenCondition:1];
        NSLog(@"條件加鎖--1--%@",[NSThread currentThread]);
        NSLog(@"condition-1-%ld",conditionLock.condition);
        sleep(2);
        [conditionLock unlock];
        NSLog(@"解鎖--1--%@",[NSThread currentThread]);
    });
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [conditionLock lockWhenCondition:0];
        NSLog(@"條件加鎖--0--%@",[NSThread currentThread]);
        NSLog(@"condition-0-%ld",conditionLock.condition);
        sleep(2);
        [conditionLock unlockWithCondition:1];
        NSLog(@"解鎖--0--%@",[NSThread currentThread]);
    });
運行結(jié)果
  • NSCondition:在給定線程中充當鎖和檢查點劲件,會阻塞線程

[condition signal]:喚醒最先進入wait的線程
[condition broadcast]:換線所有進入wait狀態(tài)的線程

    NSCondition *condition = [[NSCondition alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"加鎖 --1--%@",[NSThread currentThread]);
        [condition wait];
        NSLog(@"wait-1-%@",[NSThread currentThread]);
        [condition unlock];
        NSLog(@"解鎖 --wait1--%@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"加鎖 --2--%@",[NSThread currentThread]);
        [condition wait];
        NSLog(@"wait-2-%@",[NSThread currentThread]);
        [condition unlock];
        NSLog(@"解鎖 --wait2--%@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"加鎖 ----%@",[NSThread currentThread]);
        [condition signal];
        NSLog(@"喚醒 ----%@",[NSThread currentThread]);
        [condition unlock];
        NSLog(@"解鎖 ----%@",[NSThread currentThread]);
    });
運行結(jié)果
  • @synchronized:修飾變量(對象)掸哑,確保變量的線程安全,它能自動為修飾的變量創(chuàng)建互斥鎖或解鎖
//    @property(nonatomic, assign) NSInteger count;
    static void (^myBlock)();
    myBlock = ^(){
        @synchronized(self){
            self.count += 1;// 如果不使用@synchronized零远,可能出現(xiàn)同時被修改的情況苗分,即相同的值;
        };
    };
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        myBlock();
        NSLog(@"1--%ld--%@",self.count,[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        myBlock();
        NSLog(@"2--%ld--%@",self.count,[NSThread currentThread]);
    });
運行結(jié)果

線程通信

一個線程傳遞數(shù)據(jù)給另一個線程牵辣;一個線程執(zhí)行完特定任務之后摔癣,執(zhí)行另一個線程;

  • NSThread

[self performSelectorOnMainThread:@selector(run:) withObject:@"主線程" waitUntilDone:YES];   
[self performSelector:@selector(run:) onThread:thread withObject:@"指定線程" waitUntilDone:YES];   
[self performSelector:@selector(run:) withObject:@"當前線程"];
  • GCD

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"其他操作");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"進行UI刷新操作");
        });
    });
  • NSOperation

    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"一些操作%@",[NSThread currentThread]);
        [mainQueue addOperationWithBlock:^{
            NSLog(@"刷新UI%@",[NSThread currentThread]);
        }];
    }];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纬向,一起剝皮案震驚了整個濱河市择浊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逾条,老刑警劉巖琢岩,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異师脂,居然都是意外死亡担孔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門吃警,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糕篇,“玉大人,你說我怎么就攤上這事酌心“柘” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵安券,是天一觀的道長拼坎。 經(jīng)常有香客問我,道長完疫,這世上最難降的妖魔是什么泰鸡? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮壳鹤,結(jié)果婚禮上盛龄,老公的妹妹穿的比我還像新娘。我一直安慰自己芳誓,他們只是感情好余舶,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锹淌,像睡著了一般匿值。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赂摆,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天挟憔,我揣著相機與錄音钟些,去河邊找鬼。 笑死绊谭,一個胖子當著我的面吹牛政恍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播达传,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼篙耗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宪赶?” 一聲冷哼從身側(cè)響起宗弯,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搂妻,沒想到半個月后蒙保,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡叽讳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坟募。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岛蚤。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖懈糯,靈堂內(nèi)的尸體忽然破棺而出涤妒,到底是詐尸還是另有隱情,我是刑警寧澤赚哗,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布她紫,位于F島的核電站,受9級特大地震影響屿储,放射性物質(zhì)發(fā)生泄漏贿讹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一够掠、第九天 我趴在偏房一處隱蔽的房頂上張望民褂。 院中可真熱鬧,春花似錦疯潭、人聲如沸赊堪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哭廉。三九已至,卻和暖如春相叁,著一層夾襖步出監(jiān)牢的瞬間遵绰,已是汗流浹背辽幌。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留街立,地道東北人舶衬。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像赎离,于是被迫代替她去往敵國和親逛犹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容