iOS多線程-線程同步/線程安全

同步任務(wù)線程同步是兩個(gè)概念。不要搞混了。一定要區(qū)分當(dāng)前線程所在線程的關(guān)系。

同步任務(wù):串行執(zhí)行任務(wù),會(huì)把你的同步代碼一行一行的執(zhí)行下去,即使是在block里面,也會(huì)等待任務(wù)完成,會(huì)阻塞當(dāng)前線程。
異步任務(wù):并行執(zhí)行的任務(wù),代碼會(huì)放在另外一塊區(qū)域去執(zhí)行,不會(huì)會(huì)阻塞當(dāng)前線程,不會(huì)等待執(zhí)行完畢返回結(jié)果。
異步線程:并行執(zhí)行的任務(wù)的線程。
線程同步:是指多個(gè)線程同時(shí)訪問一個(gè)資源時(shí)可能存在競(jìng)爭(zhēng)問題提供的解決方案,使多個(gè)線程可以對(duì)同一個(gè)資源進(jìn)行操作,比如線程A為數(shù)組M添加了一個(gè)數(shù)據(jù),線程B可以接收到添加數(shù)據(jù)后的數(shù)組M。線程同步就是線程之間相互的通信。

常見比如多個(gè)線程內(nèi)操作了同一個(gè)變量,這個(gè)時(shí)候一定要考慮線程安全和同步。

- (void)removeLastIamgeName{//假如每個(gè)進(jìn)來(lái)的都是不同的線程
    //self.imageNames是NSMutableArray
    if (self.imageNames.count>0) {
        //比如當(dāng)前count為1,那么第一個(gè)線程和第二個(gè)線程都可以進(jìn)入判斷內(nèi)部,第一個(gè)線程刪除了數(shù)組里面最后一個(gè)數(shù)據(jù),第二個(gè)線程去刪除的時(shí)候因?yàn)橐呀?jīng)沒有數(shù)據(jù)了,count=0,這個(gè)時(shí)候取調(diào)用removeObjectAtIndex:0機(jī)會(huì)crash,數(shù)組越界了
        [self.imageNames removeObjectAtIndex:self.imageNames.count-1];
    }
}

同步異步任務(wù)

這里有個(gè)概念容易混攪:
使用GCD創(chuàng)建一個(gè)并行隊(duì)列,如果向并行隊(duì)列添加同步任務(wù),它并不是串行執(zhí)行的。任務(wù)對(duì)于當(dāng)前線程是同步串行執(zhí)行的,對(duì)于隊(duì)列來(lái)說是并行執(zhí)行的,只是隊(duì)列里面可能每次都只有一個(gè)任務(wù),所以看起來(lái)是串行的。
GCD的隊(duì)列定義了隊(duì)列里面的任務(wù)是否支持并行,并沒有定義任務(wù)在當(dāng)前線程是同步還是異步。

    dispatch_queue_t globalQueue = dispatch_queue_create([@"com.yasin.dispatchqueue" cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);;
    for (int i=0; i<10; i++) {
        dispatch_sync(globalQueue, ^(){
            NSLog(@"%d",i);
             /**
                 *  i從0到9輸出,因?yàn)楫?dāng)前線程是串行的,會(huì)等待同步任務(wù)執(zhí)行完畢
                 如果在for循環(huán)內(nèi)部再起一個(gè)異步線程,在異步線程內(nèi)執(zhí)行dispatch_sync(globalQueue, ^(),輸出就是亂序的
                 */
        });
    }

這里會(huì)一個(gè)接著一個(gè)執(zhí)行同步線程的任務(wù),并不會(huì)出現(xiàn)幾個(gè)線程同時(shí)執(zhí)行的現(xiàn)象。當(dāng)前線程規(guī)定了要同步一個(gè)一個(gè)執(zhí)行線程任務(wù)怎么可能會(huì)并行執(zhí)行多個(gè)任務(wù)。這個(gè)代碼書寫邏輯就不對(duì)。

--小結(jié)-- 其實(shí)只要不做傻事就不會(huì)出問題,要一個(gè)一個(gè)執(zhí)行的任務(wù)就放在串行隊(duì)列里面,需要異步就異步,需要同步就同步(異步不卡當(dāng)前線程,同步卡當(dāng)前線程);如果想要并行執(zhí)行多個(gè)任務(wù),就放在并行隊(duì)列里面,開異步線程去做。

線程同步的方法

  • 原子操作
    我們?cè)诼暶饕粋€(gè)變量的時(shí)候一般會(huì)使用nonatomic,這個(gè)就是非原子操作;原子操作是atomic。
    簡(jiǎn)單的加減使用原子操作具有更高的性能優(yōu)勢(shì)。注意是加減,不是增刪!!
    也就是說僅僅對(duì)于getter,setter是線程安全的,兩個(gè)線程都去對(duì)變量賦值是安全的。對(duì)于比如NSMutableArray類型的增刪操作不是線程安全的

  • 線程鎖
    鎖可以保護(hù)臨界區(qū),代碼在臨界區(qū)同一時(shí)間只會(huì)被一個(gè)線程執(zhí)行。有互斥鎖、遞歸鎖、讀寫鎖、分布鎖、自旋鎖、雙重檢查鎖等等。
    后續(xù)會(huì)重點(diǎn)介紹這部分

  • 條件、信號(hào)量
    有個(gè)BOOL類型的變量,當(dāng)線程A進(jìn)入臨界區(qū)時(shí)把BOOL值置為NO,如果線程B準(zhǔn)備進(jìn)入臨界區(qū)時(shí)發(fā)現(xiàn)BOOL值為NO就掛起等待,當(dāng)線程A出臨界區(qū)時(shí)把BOOL置為YES,線程B會(huì)被喚醒并繼續(xù)執(zhí)行。
    條件就是使用信號(hào)量在線程之間相互發(fā)生信號(hào)。
    條件通常被使用來(lái)說明資源可用性,或用來(lái)確保任務(wù)以特定的順序執(zhí)行。

  • 使用Selector
    selector方法允許你的線程以異步的方式來(lái)傳遞消息,以確保它們?cè)谕粋€(gè)線程上面執(zhí)行是同步的。
    比如NSObject中的方法

performSelector:withObject:afterDelay:
performSelectorInBackground:withObject:
performSelector:onThread:withObject:waitUntilDone:

代碼:

[self performSelector:@selector(test:) withObject:nil afterDelay:1];
[self performSelectorInBackground:@selector(test:) withObject:nil];
//等效于[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:nil];

代碼2:

-(void)viewDidLoad
{
    [super viewDidLoad];
    
    [self threadInfo:@"UI"];
    
    _isNewThreadAborted = NO;
    _thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread:) object:nil];
    //開始線程
    [_thread start];
    //在另一個(gè)線程中的Run Loop中執(zhí)行Selector
    [self performSelector:@selector(test:) onThread:_thread withObject:nil waitUntilDone:NO];
}
//在新線程中創(chuàng)建并開始一個(gè)NSRunLoop
-(void)newThread:(id)obj
{
    @autoreleasepool
    {
        NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
        while (!_isNewThreadAborted)
        {
            [currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        NSLog(@"線程停止");
    }
}
//Selector執(zhí)行
-(void)test:(id)obj
{
    [self threadInfo:@"test"];
    _isNewThreadAborted = YES;
}
-(void)threadInfo:(NSString*)category
{
    NSLog(@"%@ - %@", category, [NSThread currentThread]);
}
  • 內(nèi)存屏障和 Volatile 變量
    OSMemoryBarrier函數(shù),設(shè)置內(nèi)存屏蔽
    volatile變量
    因?yàn)閮?nèi)存屏障和volatile變量降低了編譯器可執(zhí)行的優(yōu)化,因此你應(yīng)該謹(jǐn)慎使用它們,只在有需要的地方時(shí)候,以確保正確性。
    這部分涉及編譯器,現(xiàn)在還不是很理解,以后再補(bǔ)充

并行并發(fā)

并發(fā)編程、并發(fā)程序,和并行計(jì)算機(jī)。
并發(fā)性與軟件結(jié)構(gòu)有關(guān),而并行性與硬件有關(guān)。
也就是說,并發(fā)就是多線程編程,并行就是多核處理器。

天下武功出少林

這里推薦一個(gè)特別好的文章iOS多線程編程指南(四)線程同步

后續(xù)我會(huì)著重研究線程鎖這一塊

最后編輯于
?著作權(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)容