華為倉(cāng)頡語(yǔ)言初識(shí):并發(fā)編程之同步機(jī)制(上)

前言

線程同步機(jī)制是多線程下解決線程對(duì)共享資源競(jìng)爭(zhēng)的主要方式,華為倉(cāng)頡語(yǔ)言提供了三種常見(jiàn)的同步機(jī)制用來(lái)保證線程同步安全,分別是原子操作,互斥鎖和條件變量。本篇文章詳細(xì)介紹主要倉(cāng)頡語(yǔ)言解決同步機(jī)制的方法,建議點(diǎn)贊收藏!

同步機(jī)制

原子操作

和 java 一樣,倉(cāng)頡也支持使用原子操作(Atomic)用來(lái)確保多線程下的數(shù)據(jù)訪問(wèn)安全。主要是提供整數(shù)類(lèi)型,布爾類(lèi)型和引用類(lèi)型三種方式。

以整數(shù)類(lèi)型為例,原子變量 Atomic 包括 8 位(AtomicInt8) 至 64 位(AtomicInt64)的整數(shù)類(lèi)型,同時(shí)支持基本數(shù)據(jù)的讀寫(xiě)。

不使用原子操作,在多線程情況下對(duì)數(shù)據(jù)進(jìn)行累加。

? var sum: Int64 = 0

? for (pattern in 1..100) {

? ? ? ? spawn {

? ? ? ? ? sum += 1? ? ? ? ? ? ?

? ? ? ? ? ? }

? ? ? ? }

? sleep(Duration.second*2)

AppLog.info("Main===${sum}")

//輸出? Main===96

使用原子變量 AtomicInt64 對(duì)數(shù)據(jù)進(jìn)行累加。

? var sum = AtomicInt64(0)

? for (pattern in 1..100) {

? ? ? ? spawn {

? ? ? ? ? sum.fetchAdd(1)

? ? ? ? ? ? }

? ? ? ? }

? sleep(Duration.second*2)

? AppLog.info("Main===${sum.load()}")

//輸出? Main===99

原子操作 Atomic 使用compareAndSwap 用于數(shù)據(jù)的交換,使用的和 Java 中一樣的 CAS 同步機(jī)制用于確保在多線程的情況下能夠交換成功。

可重入互斥鎖

倉(cāng)頡語(yǔ)言中同樣支持使用可重入互斥鎖(ReentrantMutex)來(lái)解決多線程的同步問(wèn)題。當(dāng)一個(gè)線程獲取到共享變量的鎖時(shí),在該線程釋放鎖之前,其他線程都無(wú)法訪問(wèn)該共享變量,直到該線程持有的同步鎖釋放。

ReentrantMutex 可重入互斥鎖主要提供了三個(gè)方法 ,分別是 lock(),unlock(),tryLock()。lock()和 unlock()總是成對(duì)出現(xiàn)的,及對(duì)共享變量加完鎖后,等使用結(jié)束必須及時(shí)釋放鎖。

lock()和unlock()

還是以在多線程下對(duì)數(shù)據(jù)進(jìn)行累加操作。

? var sum = AtomicInt64(0)

? let mutex =? ReentrantMutex()

? for (pattern in 1..100) {

? ? ? ? spawn {

? ? ? ? ? ? mutex.lock()

? ? ? ? ? ? sum +=1

? ? ? ? ? ? mutex.unlock()

? ? ? ? ? ? }

? ? ? ? }

? sleep(Duration.second*2)

? AppLog.info("Main===${sum}")

//輸出? Main===99

ReentrantMutex 作為可重入互斥鎖,當(dāng)已經(jīng)獲取互斥鎖的線程再次獲取該互斥鎖時(shí),可以直接獲取。但是該線程獲取幾次互斥鎖就需要釋放幾次鎖。

tryLock()

tryLock 表示線程嘗試去獲取鎖,但是并一定能夠獲取到??梢酝ㄟ^(guò) tryLock()返回到布爾值判斷該線程釋放獲取到鎖,然后調(diào)用 unLock 釋放鎖。

Monitor

Monitor 是一個(gè)內(nèi)置鎖,繼承于ReentrantMutex 可重入互斥鎖。Monitor 不僅可以使用 lock(),unlock(),tryLock() 還提供了 wait(),notify(),notifyAll()三個(gè)方法用于解決線程間的數(shù)據(jù)安全問(wèn)題,這一點(diǎn)和 java 不同,java 中的 Object 提供了 wait(),notify(),notifyAll()三個(gè)方法。而 在倉(cāng)頡中是單獨(dú)封裝的類(lèi)。

下面舉例說(shuō)明:

let fun = spawn {

? ? ? ? ? ? ? ? monitor.lock()

? ? ? ? ? ? ? ? while (flag) {

? ? ? ? ? ? ? ? AppLog.info("Main=== thread 1 開(kāi)始執(zhí)行")

? ? ? ? ? ? ? ? monitor.wait()

? ? ? ? ? ? ? ? AppLog.info("Main=== thread 1 執(zhí)行結(jié)束")


? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? monitor.unlock()

? ? ? ? ? ? ? ? }

? sleep(Duration.second)

? monitor.lock()

? AppLog.info("Main=== 主線程開(kāi)始執(zhí)行")

? flag = false? ? ? ? ? ? ? ? ? ?

? AppLog.info("Main===主線程執(zhí)行結(jié)束")? ? ? ? ? ? ? ? ? ?

? monitor.notifyAll()? ? ? ? ? ? ? ? ? ?

? monitor.unlock()? ? ? ? ? ? ? ? ? ?

? fun.get()? ? ?

//輸出

// Main=== thread 1 開(kāi)始執(zhí)行

// Main=== 主線程開(kāi)始執(zhí)行

// Main===主線程執(zhí)行結(jié)束

// Main=== thread 1 執(zhí)行結(jié)束

需要注意的是 monitor 的 wait 和 notify/notifyAll 方法使用之前必須要先獲取到鎖,即 lock()。

總結(jié)

倉(cāng)頡中的多線程并發(fā)安全同步機(jī)制十分重要,對(duì)于會(huì) Java 的小伙伴來(lái)說(shuō)簡(jiǎn)單容易上手,但是也有一些需要注意的點(diǎn),比如 notify 調(diào)用前必須要獲取到線程鎖,用完必須釋放否則會(huì)導(dǎo)致其他線程無(wú)法獲取到鎖,本篇文章就先講這些,已經(jīng)學(xué)會(huì)了的小伙伴,趕快動(dòng)手試試吧!。

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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