0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

線程同步技術(shù)介紹

科技綠洲 ? 來(lái)源:Linux開(kāi)發(fā)架構(gòu)之路 ? 作者:Linux開(kāi)發(fā)架構(gòu)之路 ? 2023-11-13 14:19 ? 次閱讀

正文:首先看一下問(wèn)題引出,先看一些經(jīng)典的問(wèn)題.

多線程的隱患

首先我們利用多線程的話肯定是好處多多,因?yàn)槲覀兛梢酝瑫r(shí)去做一些事情,大大的提高了效率.像我們下載視頻的時(shí)候就可以同時(shí)下載多個(gè)視頻,這樣是節(jié)省了很多時(shí)間,用戶(hù)體驗(yàn)也會(huì)更好.但是用得時(shí)候也會(huì)存在一些安全隱患,比如同一塊資源可能會(huì)被多個(gè)線程共享,也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源,這樣會(huì)出現(xiàn)一些數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全的問(wèn)題.下面我們就看一些例子.

存錢(qián)取錢(qián)案例

比如我現(xiàn)在有1000元,同時(shí)有2個(gè)線程去處理,一個(gè)線程是取錢(qián)100元,一個(gè)線程是存錢(qián)100元,我制作了一張示意圖如下:

圖片

存錢(qián)取錢(qián)示意圖

我們從上面的圖應(yīng)該很清楚,存100、取100的最終結(jié)果就是還是剩余1000元,但是看我們上面的示意圖,最終結(jié)果要么是900要么是1100,這就與1000的結(jié)果對(duì)不上,所以很明顯用多線程是會(huì)存在隱患,下面我們用代碼演示一下上面的結(jié)果:

圖片

賣(mài)票案例

這個(gè)和上面的稍微有點(diǎn)差別,因?yàn)樯厦娴氖?個(gè)操作,而賣(mài)票呢,它是1個(gè)操作.同樣的比如我現(xiàn)在有1000張票,同時(shí)有2個(gè)線程去處理賣(mài)票,一個(gè)線程是一個(gè)線程賣(mài)票100張,另一個(gè)線程也是賣(mài)票100張,同時(shí)操作的話,也會(huì)出現(xiàn)異常,我制作了一張示意圖如下:

圖片

我們同樣的也可以用代碼演示一下效果:

圖片

上面的兩個(gè),大家可以試試.接下來(lái)針對(duì)上面的問(wèn)題,我們就引出了今天的主角,線程同步技術(shù)

線程同步技術(shù)

解決方案:使用線程同步技術(shù) (同步,就是協(xié)同步調(diào),按照預(yù)定的先后次序進(jìn)行運(yùn)行),我們先來(lái)看下線程同步技術(shù)有哪些方案:線程同步方案最常見(jiàn)的技術(shù)就是:加鎖.大概方案如下(大致這么多方案,當(dāng)然還有其他的)

OSSpinLock (自旋鎖)

os_unfair_lock (互斥鎖)

pthread_mutex (互斥鎖、遞歸鎖)(里面3種類(lèi)型,目前只說(shuō)2種對(duì)我們有用的)

dispatch_queue (DISPATCH_QUEUE_SERIAL)

NSLock

NSRecursiveLock

NSCondition

NSConditionLock

@synchronized

以上的這些都是可以做到線程同步方案,我會(huì)一個(gè)一個(gè)介紹,并且介紹它們的優(yōu)缺點(diǎn),性能怎么樣,我們?cè)趺慈ミx擇等等.我們就以上面的例子講解.

OSSpinLock (自旋鎖)

OSSpinLock 叫做 "自旋鎖", 等待鎖的狀態(tài)會(huì)處于忙等( busy-wait )狀態(tài),一直占用著CPU內(nèi)存.頭文件導(dǎo)入,而且這個(gè)鎖是過(guò)期鎖,iOS10以后就過(guò)期了,但是我們還是來(lái)看看,因?yàn)槊嬖囍锌赡軙?huì)遇到,用法如下:

初始化鎖:OSSpinLock spinLock = OS_SPINLOCK_INIT;

加鎖: OSSpinLockLock(&_spinLock);

解鎖: OSSpinLockUnlock(&_spinLock);

我們先看賣(mài)票:

圖片

我是加鎖和解鎖了,為什么上面的代碼還有問(wèn)題,有發(fā)現(xiàn)原因的嗎?這是因?yàn)镾pinLock是局部變量,所以我們進(jìn)去都是初始化了一把新鎖,這把鎖并沒(méi)有被使用過(guò),是達(dá)不到加鎖的目的.所以所有的線程都是用同一把鎖才能達(dá)到加鎖的目的.請(qǐng)看下面代碼:

圖片

確實(shí)是剩下85張,沒(méi)有問(wèn)題.原理是這樣,每次執(zhí)行saleTicket都會(huì)進(jìn)入 //加鎖: OSSpinLockLock(&_SpinLock) 這個(gè)代碼,第一次進(jìn)來(lái)是正常給_SpinLock加鎖,第二次進(jìn)來(lái)的時(shí)候,發(fā)現(xiàn)_SpinLock已經(jīng)被人加了鎖,它會(huì)在這邊等待,等待這把鎖被解鎖,解鎖完了以后然后它再去加鎖,就這樣依次進(jìn)行,就保證了里面的那段代碼同時(shí)只有一個(gè)線程在處理.這就解決了線程同步的問(wèn)題.

接下來(lái)我們就看存錢(qián)和取錢(qián)的問(wèn)題.存錢(qián)、取錢(qián)是2個(gè)操作,我們是用同一把鎖還是2把鎖?思考了這個(gè)問(wèn)題我們就知道怎么做了,因?yàn)榇驽X(qián)和取錢(qián)是不能同時(shí)進(jìn)行,所以我們就用同一把鎖即可(2把鎖是有問(wèn)題的大家可以自己試試),請(qǐng)看下面的代碼,我們驗(yàn)證一下:

圖片

自旋鎖"忙等"狀態(tài)是怎么等呢?忙等就是一直忙碌,而且還在等待,類(lèi)似這樣while(鎖還沒(méi)有解開(kāi)),就會(huì)一直執(zhí)行,占用cpu,直到鎖被放開(kāi).而且OSSpinLock是已經(jīng)過(guò)期了,而且目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)的問(wèn)題.

下面說(shuō)一下線程的調(diào)度問(wèn)題

圖片

其實(shí)你看上面的圖,如果隨著時(shí)間的推移,操作系統(tǒng)把時(shí)間給thread1一點(diǎn)時(shí)間,再給thread2一點(diǎn)時(shí)間,再給thread3一點(diǎn)時(shí)間,而且這個(gè)時(shí)間周期非常短,就這樣一直非??斓那袚Q,這樣下來(lái)給我們的感覺(jué)就是同時(shí)執(zhí)行.這就是實(shí)現(xiàn)多線程的一個(gè)方案.也就是多線程的原理,我們也可以說(shuō)這是時(shí)間片輪轉(zhuǎn)調(diào)度算法.調(diào)用進(jìn)程或者線程都是用這套算法

還有個(gè)就是線程的優(yōu)先級(jí)問(wèn)題,比如thread1的優(yōu)先級(jí)比較高,那么操作系統(tǒng)就會(huì)給thread1多一點(diǎn)時(shí)間去執(zhí)行.其他的線程就少一點(diǎn)時(shí)間去執(zhí)行.這樣的話,我們使用自旋鎖就會(huì)存在一個(gè)優(yōu)先級(jí)反轉(zhuǎn)的問(wèn)題.比如thread1優(yōu)先級(jí)非常高,thread2優(yōu)先級(jí)很低.首先是thread2先進(jìn)去加鎖,thread1再進(jìn)來(lái)就會(huì)等thread2解鎖,由于thread1的優(yōu)先級(jí)非常高,CPU就把大量的時(shí)間給了thread1,此時(shí)可能導(dǎo)致thread2沒(méi)有時(shí)間執(zhí)行解鎖,thread1就會(huì)一直執(zhí)行等待,有點(diǎn)死鎖的感覺(jué).

這樣大家想一想:如果優(yōu)先級(jí)高的不是忙等,而是休眠,休息就不會(huì)占用CPU,那不就是解決了這個(gè)問(wèn)題.

os_unfair_lock (互斥鎖)

os_unfair_lock用于取代不安全OSSpinLock,是從iOS10開(kāi)始支持.

從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài),并非忙等(后面會(huì)證明一下)

它的用法和OSSpinLock非常像,需要倒入頭文件,用法如下:

初始化鎖:os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;

加鎖: os_unfair_lock_lock(&_unfairLock);

解鎖:os_unfair_lock_unlock(&_unfairLock);

下面我們就去看一下用法

圖片

存錢(qián)和取錢(qián)也是一樣的道理,我們可以自己試試.

pthread_mutex (互斥鎖)

像這種pthread開(kāi)頭的一般都是跨平臺(tái)的Windows、linux等等都是可以用的,mutex叫做"互斥鎖",等待鎖的線程會(huì)處于休眠狀態(tài)

其實(shí)用法都是差不多,我們先來(lái)看下怎么用,這個(gè)稍微代碼多一點(diǎn)點(diǎn)

//初始化屬性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
    //初始化鎖
    pthread_mutex_init(&_mutex, &attr);(&attr也可以傳NULL,這樣的話,上面的都是默認(rèn)的,上面可以都不用寫(xiě))
    //加鎖
    pthread_mutex_lock(&_mutex);
    //解鎖
    pthread_mutex_unlock(&_mutex);
    //銷(xiāo)毀相關(guān)資源
    pthread_mutexattr_destroy(&attr);
    pthread_mutex_destroy(&_mutex);

其中PTHREAD_MUTEX_NORMAL是鎖的類(lèi)型,后面會(huì)細(xì)說(shuō),先傳默認(rèn)PTHREAD_MUTEX_NORMAL

先看運(yùn)行結(jié)果:

圖片

沒(méi)有問(wèn)題,記得銷(xiāo)毀哈,之前說(shuō)的2個(gè)鎖,沒(méi)有提供銷(xiāo)毀的方法,那我們就不寫(xiě),如果提供了,我們還是寫(xiě)一下的好!

pthread_mutex (遞歸鎖)

我們?cè)倏戳硪环N情況,請(qǐng)看下面的代碼:

圖片

上面這些代碼會(huì)出現(xiàn)什么情況?死鎖,會(huì)出現(xiàn)相互等待的情況,只會(huì)輸出第一個(gè)NSLog,遇到這種情況我們?cè)趺唇鉀Q才好呢?2把不同的鎖即可解決問(wèn)題,就是otherMutexTest里面一把鎖,otherMutexTest2里面另一把鎖就可以解決了,這個(gè)我就不截圖了,我們可以自己試試

再看下面另一種情況:出現(xiàn)遞歸怎么辦?如下圖

圖片

遇到上面的這種情況我們又怎么處理,如果就像截圖那樣的話,就會(huì)出現(xiàn)休眠等待.如果我們想執(zhí)行下去,我們就是可以設(shè)置鎖的類(lèi)型來(lái)解決這個(gè)問(wèn)題:一共3種類(lèi)型如下

圖片

我們只要把鎖的類(lèi)型換成遞歸鎖,立刻就能解決這個(gè)問(wèn)題,我們加一個(gè)遞歸停止條件,不然會(huì)一直運(yùn)行

圖片

還有一個(gè)注意的,這個(gè)允許重復(fù)加鎖,一定是在同一個(gè)線程,如果是多個(gè)線程的話,就不行.遞歸鎖:允許同一個(gè)線程對(duì)一把鎖重復(fù)加鎖.

自旋鎖、互斥鎖匯編分析

自旋鎖:一直忙等,占用CPU內(nèi)存,一直在執(zhí)行代碼;互斥鎖:不等待,休眠.不執(zhí)行代碼.我們?cè)趺慈プC明這個(gè)問(wèn)題呢?我們可以從匯編實(shí)現(xiàn)上去證明這個(gè)問(wèn)題,我們先看OSSpinLock自旋鎖:

圖片

首先我們?nèi)绻眠@個(gè)上面的來(lái)調(diào)試的話,是看不出來(lái)什么效果的,因?yàn)檫@里面都是一大段一大段匯編代碼執(zhí)行的,我們需要一句一句的執(zhí)行匯編指令.就需要敲si,s是step的意思,代碼一行一行的執(zhí)行,如果只用s的話,就是一行oc代碼執(zhí)行,一行oc對(duì)應(yīng)可能一大段匯編,所以我們還需要加i,i是instruction的意思是一行一行匯編指令執(zhí)行,簡(jiǎn)稱(chēng)si. 還有個(gè)是nexti,它也是一行一行匯編指令執(zhí)行,只是nexti它是遇到函數(shù)就會(huì)一下執(zhí)行過(guò)去.因?yàn)槲覀円春瘮?shù)實(shí)現(xiàn),所以我們用si.

我們?cè)倏匆幌?我代碼是怎么寫(xiě)的:

圖片

我是創(chuàng)建了10個(gè)線程去執(zhí)行賣(mài)票,而且在賣(mài)票中間sleep(100),這樣是為了,第一條線程進(jìn)去,我們不管,我們主要看第二條線程在這等待的時(shí)間,到底做了什么事.所以我們主要看第二條的匯編代碼.sleep(100)是為了時(shí)間長(zhǎng)點(diǎn),方便我們能看出做什么事.如果時(shí)間太短,直接第二條線程就不等待,那我們就看不到效果,請(qǐng)看下面的結(jié)果

圖片

從上面的結(jié)果看,進(jìn)入OSSpinLockLock函數(shù),它會(huì)一直在81aef那里一直循環(huán)執(zhí)行,這是外循環(huán),我們所說(shuō)的自旋鎖就是這樣,一直循環(huán)執(zhí)行,占用CPU內(nèi)存.一旦有人放開(kāi)這把鎖就會(huì)條件循環(huán)結(jié)束,不會(huì)再執(zhí)行循環(huán).

接下來(lái)我們看看互斥鎖pthread_mutex

查找的方法和上面的一樣,我就截圖最關(guān)鍵的圖即可,請(qǐng)看下面:

圖片

執(zhí)行到最后,直接是callsys,調(diào)用系統(tǒng)的方法,是不是類(lèi)似我之前說(shuō)的runloop里面的休眠的方法,而且我們知道休眠是任何事情都不會(huì)做,不占用CPU內(nèi)存,所以我們最后看到,我的模擬器立刻又彈出來(lái)了,說(shuō)明確實(shí)是睡眠,不占用任何CPU內(nèi)存.

os_unfair_lock_lock我們可以用上面的方法嘗試,它的結(jié)果也是互斥鎖.

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    6810

    瀏覽量

    88743
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    6684

    瀏覽量

    123140
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68236
  • 多線程同步
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    5229
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    一文詳解Linux線程同步

    我們?cè)诠ぷ髦袝?huì)經(jīng)常遇到線程同步,那么到底什么是線程同步呢,線程同步的本質(zhì)是什么,
    發(fā)表于 08-25 11:49 ?596次閱讀

    線程編程之四 線程同步

    介紹最常用的四種:臨界區(qū)(CCriticalSection) 事件(CEvent) 互斥量(CMutex) 信號(hào)量(CSemaphore)  通過(guò)這些類(lèi),我們可以比較容易地做到線程同步。A
    發(fā)表于 10-22 11:43

    Linux多線程線程同步

    。同一進(jìn)程內(nèi)的線程共享進(jìn)程的地址空間。通信:進(jìn)程間通信IPC,線程間可以直接讀寫(xiě)進(jìn)程數(shù)據(jù)段(如全局變量)來(lái)進(jìn)行通信——需要進(jìn)程同步和互斥手段的輔助,以保證數(shù)據(jù)的一致性。調(diào)度和切換:線程
    發(fā)表于 12-08 14:14

    IOT-OS之RT-Thread--- 線程同步線程間通信

    rt_thread,下面要介紹線程間的同步與通信,線程同步對(duì)象rt_sem / rt_mutex / rt_event和
    發(fā)表于 07-02 06:15

    QNX環(huán)境下多線程編程

    介紹了QNX 實(shí)時(shí)操作系統(tǒng)和多線程編程技術(shù),包括線程同步的方法、多線程程序的分析步驟、
    發(fā)表于 08-12 17:37 ?30次下載

    Linux多線程同步方法

    線程對(duì)共享相同內(nèi)存操作時(shí),就會(huì)出現(xiàn)多個(gè)線程對(duì)同一資源的使用,為此,需要對(duì)這些線程進(jìn)行同步,以確保它們?cè)谠L問(wèn)共享內(nèi)存的時(shí)候不會(huì)訪問(wèn)到無(wú)效的數(shù)值。
    發(fā)表于 08-08 14:17 ?2046次閱讀

    Win32多線程同步技術(shù)淺析

    簡(jiǎn)要介紹了在Win32環(huán)境下多線程訪問(wèn)共享資源時(shí)的同步機(jī)制,討論了主要的4種同步對(duì)象(臨界區(qū)、互斥元、事件、信號(hào)量),并描述了它們的優(yōu)缺點(diǎn),給出了使用Win32 API函數(shù)操控這4種對(duì)
    發(fā)表于 11-14 10:55 ?31次下載
    Win32多<b class='flag-5'>線程</b><b class='flag-5'>同步</b><b class='flag-5'>技術(shù)</b>淺析

    java多線程同步方法

    二、為什么要線程同步 因?yàn)楫?dāng)我們有多個(gè)線程要同時(shí)訪問(wèn)一個(gè)變量或?qū)ο髸r(shí),如果這些線程中既有讀又有寫(xiě)操作時(shí),就會(huì)導(dǎo)致變量值或?qū)ο蟮臓顟B(tài)出現(xiàn)混亂,從而導(dǎo)致程序異常。舉個(gè)例子,如果一個(gè)銀行賬戶(hù)
    發(fā)表于 09-27 13:19 ?0次下載

    C#多線程技術(shù)

    C#和.NET類(lèi)庫(kù)為開(kāi)發(fā)多線程應(yīng)用程序提供了很方便的支持,本章首先簡(jiǎn)要介紹.NET類(lèi)庫(kù)中的Thread類(lèi)及各種線程支持,再通過(guò)示例說(shuō)明線程使用中需要掌握的規(guī)則,最后論述
    發(fā)表于 04-23 11:32 ?15次下載

    各型號(hào)的DSPBIOS線程同步原語(yǔ)以及相關(guān)的問(wèn)題和約束

    DSP編程已經(jīng)從單循環(huán)程序過(guò)渡到使用實(shí)時(shí)特征的復(fù)雜多線程應(yīng)用程序。多線程應(yīng)用程序需要同步對(duì)共享資源的訪問(wèn)。 DSP/BIOS提供了同步線程
    發(fā)表于 05-03 09:24 ?4次下載
    各型號(hào)的DSPBIOS<b class='flag-5'>線程</b><b class='flag-5'>同步</b>原語(yǔ)以及相關(guān)的問(wèn)題和約束

    了解Linux多線程線程同步

    進(jìn)程間通信IPC,線程間可以直接讀寫(xiě)進(jìn)程數(shù)據(jù)段(如全局變量)來(lái)進(jìn)行通信——需要進(jìn)程同步和互斥手段的輔助,以保證數(shù)據(jù)的一致性。
    發(fā)表于 04-23 14:23 ?708次閱讀
    了解Linux多<b class='flag-5'>線程</b>及<b class='flag-5'>線程</b>間<b class='flag-5'>同步</b>

    使用C#實(shí)現(xiàn)Interlocked線程同步的程序免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是使用C#實(shí)現(xiàn)Interlocked線程同步的程序免費(fèi)下載。
    發(fā)表于 07-21 08:00 ?1次下載
    使用C#實(shí)現(xiàn)Interlocked<b class='flag-5'>線程</b><b class='flag-5'>同步</b>的程序免費(fèi)下載

    RT-Thread文檔_線程同步

    RT-Thread文檔_線程同步
    發(fā)表于 02-22 18:29 ?1次下載
    RT-Thread文檔_<b class='flag-5'>線程</b>間<b class='flag-5'>同步</b>

    線程同步的幾種方法

    線程同步是指在多個(gè)線程并發(fā)執(zhí)行的情況下,為了保證線程執(zhí)行的正確性和一致性,需要采用特定的方法來(lái)協(xié)調(diào)線程之間的執(zhí)行順序和共享資源的訪問(wèn)。下面
    的頭像 發(fā)表于 11-17 14:16 ?1102次閱讀

    線程如何保證數(shù)據(jù)的同步

    。本文將詳細(xì)介紹線程數(shù)據(jù)同步的概念、問(wèn)題、以及常見(jiàn)的解決方案。 一、多線程數(shù)據(jù)同步概念 在多線程
    的頭像 發(fā)表于 11-17 14:22 ?1108次閱讀