日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

线程安全: 互斥锁和自旋锁(10种)

發(fā)布時間:2025/6/15 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线程安全: 互斥锁和自旋锁(10种) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

無并發(fā),不編程.提到多線程就很難繞開鎖?.

iOS開發(fā)中較常見的兩類鎖:

1. 互斥鎖: 同一時刻只能有一個線程獲得互斥鎖,其余線程處于掛起狀態(tài).
2. 自旋鎖: 當某個線程獲得自旋鎖后,別的線程會一直做循環(huán),嘗試加鎖,當超過了限定的次數(shù)仍然沒有成功獲得鎖時,線程也會被掛起.

自旋鎖較適用于鎖的持有者保存時間較短的情況下,實際使用中互斥鎖會用的多一些.

1. 互斥鎖,信號量

1.遵守NSLocking協(xié)議的四種鎖

四種鎖分別是:
NSLock、NSConditionLock、NSRecursiveLock、NSCondition

NSLocking協(xié)議

public protocol NSLocking { public func lock()public func unlock() } 復(fù)制代碼

下面舉個多個售票點同時賣票的例子

var ticket = 20 var lock = NSLock()override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {let thread1 = Thread(target: self, selector: #selector(saleTickets), object: nil)thread1.name = "售票點A"thread1.start()let thread2 = Thread(target: self, selector: #selector(saleTickets), object: nil)thread2.name = "售票點B"thread2.start() }@objc private func saleTickets() {while true {lock.lock()Thread.sleep(forTimeInterval: 0.5) // 模擬延遲if ticket > 0 {ticket = ticket - 1print("\(String(describing: Thread.current.name!)) 賣出了一張票,當前還剩\(ticket)張票")lock.unlock()}else {print("oh 票已經(jīng)賣完了")lock.unlock()break;}} } 復(fù)制代碼

遵守協(xié)議后實現(xiàn)的兩個方法lock()和unlock(),意如其名.

除此之外NSLock、NSConditionLock、NSRecursiveLock、NSCondition四種互斥鎖各有其實現(xiàn):

1. 除NSCondition外,三種鎖都有的兩個方法:
// 嘗試去鎖,如果成功,返回true,否則返回falseopen func `try`() -> Bool// 在limit時間之前獲得鎖,沒有返回NOopen func lock(before limit: Date) -> Bool 復(fù)制代碼
2. NSCondition條件鎖:
// 當前線程掛起open func wait()// 當前線程掛起,設(shè)置一個喚醒時間open func wait(until limit: Date) -> Bool// 喚醒在等待的線程open func signal()// 喚醒所有NSCondition掛起的線程open func broadcast() 復(fù)制代碼

當調(diào)用wait()之后,NSCondition實例會解鎖已有鎖的當前線程,然后再使線程休眠,當被signal()通知后,線程被喚醒,然后再給當前線程加鎖,所以看起來好像wait()一直持有該鎖,但根據(jù)蘋果文檔中說明,直接把wait()當線程鎖并不能保證線程安全.

3. NSConditionLock條件鎖:

NSConditionLock是借助NSCondition來實現(xiàn)的,在NSCondition的基礎(chǔ)上加了限定條件,可自定義程度相對NSCondition會高些.

// 鎖的時候還需要滿足conditionopen func lock(whenCondition condition: Int)// 同try,同樣需要滿足conditionopen func tryLock(whenCondition condition: Int) -> Bool// 同unlock,需要滿足conditionopen func unlock(withCondition condition: Int)// 同lock,需要滿足condition和在limit時間之前open func lock(whenCondition condition: Int, before limit: Date) -> Bool 復(fù)制代碼
4. NSRecurisiveLock遞歸鎖:

定義了可以多次給相同線程上鎖并不會造成死鎖的鎖.

提供的幾個方法和NSLock類似.

2. GCD的DispatchSemaphore和柵欄函數(shù)
1. DispatchSemaphore信號量:

DispatchSemaphore中的信號量,可以解決資源搶占的問題,支持信號的通知和等待.每當發(fā)送一個信號通知,則信號量+1;每當發(fā)送一個等待信號時信號量-1,如果信號量為0則信號會處于等待狀態(tài).直到信號量大于0開始執(zhí)行.所以我們一般將DispatchSemaphore的value設(shè)置為1.

下面給出了DispatchSemaphore的封裝類

class GCDSemaphore {// MARK: 變量fileprivate var dispatchSemaphore: DispatchSemaphore!// MARK: 初始化public init() {dispatchSemaphore = DispatchSemaphore(value: 0)}public init(withValue: Int) {dispatchSemaphore = DispatchSemaphore(value: withValue)}// 執(zhí)行public func signal() -> Bool {return dispatchSemaphore.signal() != 0}public func wait() {_ = dispatchSemaphore.wait(timeout: DispatchTime.distantFuture)}public func wait(timeoutNanoseconds: DispatchTimeInterval) -> Bool {if dispatchSemaphore.wait(timeout: DispatchTime.now() + timeoutNanoseconds) == DispatchTimeoutResult.success {return true} else {return false}} } 復(fù)制代碼
2. barrier柵欄函數(shù):

柵欄函數(shù)也可以做線程同步,當然了這個肯定是要并行隊列中才能起作用.只有當當前的并行隊列執(zhí)行完畢,才會執(zhí)行柵欄隊列.

/// 創(chuàng)建并發(fā)隊列 let queue = DispatchQueue(label: "queuename", attributes: .concurrent) /// 異步函數(shù) queue.async {for _ in 1...5 {print(Thread.current)} } queue.async {for _ in 1...5 {print(Thread.current)} } /// 柵欄函數(shù) queue.async(flags: .barrier) {print("barrier") } queue.async {for _ in 1...5 {print(Thread.current)} } 復(fù)制代碼
3. 其他的互斥鎖
1. pthread_mutex互斥鎖

pthread表示POSIX thread,跨平臺的線程相關(guān)的API,pthread_mutex也是一種互斥鎖,互斥鎖的實現(xiàn)原理與信號量非常相似,阻塞線程并睡眠,需要進行上下文切換.

一般情況下,一個線程只能申請一次鎖,也只能在獲得鎖的情況下才能釋放鎖,多次申請鎖或釋放未獲得的鎖都會導(dǎo)致崩潰.假設(shè)在已經(jīng)獲得鎖的情況下再次申請鎖,線程會因為等待鎖的釋放而進入睡眠狀態(tài),因此就不可能再釋放鎖,從而導(dǎo)致死鎖.

這邊給出了一個基于pthread_mutex_t(安全的"FIFO"互斥鎖)的封裝 MutexLock

1. @synchronized條件鎖

日常開發(fā)中最常用的應(yīng)該是@synchronized,這個關(guān)鍵字可以用來修飾一個變量,并為其自動加上和解除互斥鎖.這樣,可以保證變量在作用范圍內(nèi)不會被其他線程改變.但是在swift中它已經(jīng)不存在了.其實@synchronized在幕后做的事情是調(diào)用了objc_sync中的objc_sync_enter和objc_sync_exit 方法,并且加入了一些異常判斷.

因此我們可以利用閉包自己封裝一套.

func synchronized(lock: AnyObject, closure: () -> ()) {objc_sync_enter(lock)closure()objc_sync_exit(lock) }// 使用 synchronized(lock: AnyObject) {// 此處AnyObject不會被其他線程改變 }復(fù)制代碼

2. 自旋鎖

1. OSSpinLock自旋鎖

OSSpinLock是執(zhí)行效率最高的鎖,不過在iOS10.0以后已經(jīng)被廢棄了.

詳見大神ibireme的不再安全的 OSSpinLock

2. os_unfair_lock自旋鎖

它能夠保證不同優(yōu)先級的線程申請鎖的時候不會發(fā)生優(yōu)先級反轉(zhuǎn)問題.這是蘋果為了取代OSSPinLock新出的一個能夠避免優(yōu)先級帶來的死鎖問題的一個鎖,OSSPinLock就是有由于優(yōu)先級造成死鎖的問題.

注意: 這個鎖適用于小場景下的一個高效鎖,否則會大量消耗cpu資源.

var unsafeMutex = os_unfair_lock() os_unfair_lock_lock(&unsafeMutex) os_unfair_lock_trylock(&unsafeMutex) os_unfair_lock_unlock(&unsafeMutex) 復(fù)制代碼

這邊給出了基于os_unfair_lock的封裝 MutexLock

3. 性能比較

這邊貼一張大神ibireme在iPhone6、iOS9對各種鎖的性能測試圖

本文收錄于 SwiftTips

參考:
不再安全的OSSpinLock
深入理解iOS開發(fā)中的鎖

如有疑問,歡迎留言 :-D

總結(jié)

以上是生活随笔為你收集整理的线程安全: 互斥锁和自旋锁(10种)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。