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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

001@多用派发队列,少用同步锁

發布時間:2024/4/14 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 001@多用派发队列,少用同步锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

多用派發隊列,少用同步鎖

《Effective Objective-C》中第41條: 多用派發隊列,少用同步鎖。多個線程執行同一份代碼時,很可能會造成數據不同步,這是推薦采用GCD來為代碼加鎖解決問題。

鎖與線程安全

快速回顧一下鎖

鎖(lock) 或者 互斥鎖(mutex) 是一種結構,用來保證一段代碼在同一時刻只有一個線程執行。它們通常被用來保證多線程訪問同一可變數據結構時的數據一致性。主要有下面幾種鎖:

  • 阻塞鎖(Blocking locks):常見的表現形式是當前線程會進入休眠,直到被其他線程釋放。
  • 自旋鎖(Spinlocks):使用一個循環不斷地檢查鎖是否被釋放。如果等待情況很少話這種鎖是非常高效的,相反,等待情況非常多的情況下會浪費 CPU 時間。
  • (Reader/writer locks):允許多個讀線程同時進入一段代碼,但當寫線程獲取鎖時,其他線程(包括讀取器)只能等待。這是非常有用的,因為大多數數據結構讀取時是線程安全的,但當其他線程邊讀邊寫時就不安全了。
  • 遞歸鎖(Recursive locks):允許單個線程多次獲取相同的鎖。非遞歸鎖被同一線程重復獲取時可能會導致死鎖、崩潰或其他錯誤行為。

APIs

蘋果提供了一系列不同的鎖 API,下面列出了其中一些:

  • pthread_mutex_t
  • pthread_rwlock_t
  • dispatch_queue_t
  • NSOperationQueue 當配置為 serial 時
  • NSLock
  • OSSpinLock

除此之外,Objective-C 提供了 @synchronized 語法結構,它其實就是封裝了 pthread_mutex_t 。與其他 API 不同的是,@synchronized 并未使用專門的鎖對象,它可以將任意 Objective-C 對象視為鎖。@synchronized(someObject) 區域會阻止其他 @synchronized(someObject) 區域訪問同一對象指針。不同的 API 有不同的行為和能力:

  • pthread_mutex_t 是一個可選擇性地配置為遞歸鎖的阻塞鎖;
  • pthread_rwlock_t 是一個阻塞讀寫鎖;
  • dispatch_queue_t 可以用作阻塞鎖,也可以通過使用 barrier block 配置一個同步隊列作為讀寫鎖,還支持異步執行加鎖代碼;
  • NSOperationQueue 可以用作阻塞鎖。與 dispatch_queue_t 一樣,支持異步執行加鎖代碼。
  • NSLock 是 Objective-C 類的阻塞鎖,它的同伴類 NSRecursiveLock 是遞歸鎖。
  • OSSpinLock 顧名思義,是一個自旋鎖。 最后,@synchronized 是一個阻塞遞歸鎖。

OC中的方法鎖方法

互斥鎖塊@synchronized

// 1、創建一個鎖,等待塊執行完畢 -(vodi)synchronizedMethod {@synchronized(self) {//Safe} } 復制代碼

NSLock對象

// 2、另外的一個方法時使用NSLock對象 _lock = [[NSLock alloc] init]; - (void)synchronizedMethod {[_lock lock]// Safe[_lock unlock] } 復制代碼

互斥鎖pthread_mutex_lock

/// 3、互斥鎖 YYCache pthread_mutex_init(&_lock, NULL);- (NSUInteger)totalCost {pthread_mutex_lock(&_lock);NSUInteger totalCost = _lru->_totalCost;pthread_mutex_unlock(&_lock);return totalCost; }- (void)dealloc {...//銷毀互斥鎖pthread_mutex_destroy(&_lock); } 復制代碼

信號量

_lock = dispatch_semaphore_create(1);復制代碼

然后使用了宏來代替加鎖解鎖的代碼:

#define Lock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER) #define Unlock() dispatch_semaphore_signal(self->_lock) 復制代碼

使用:

- (BOOL)containsObjectForKey:(NSString *)key {if (!key) return NO;Lock();BOOL contains = [_kv itemExistsForKey:key];Unlock();return contains; } 復制代碼

濫用@synchronized會減低代碼效率,極端情況下回出現死鎖(deadlock)現象。也可以使用NSRecursiveLock這種遞歸鎖,這樣線程多次持有該鎖,而不會出現死鎖。

雖然加了鎖,但是沒法保證絕對的線程安全。因為一個線程多次調用獲取方法(getter),每次取到的結果未必相同。在兩次操作之間,可能會有其他線程寫入新的屬性值。

隊列

把設置操作與獲取操作都安排在序列化的隊列里面執行,這樣針對屬性的訪問操作就都同步了。

串行同步隊列

_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);//讀取字符串 - (NSString*)someString {__block NSString *localSomeString;dispatch_sync(_syncQueue, ^{localSomeString = _someString;});return localSomeString; }//設置字符串 - (void)setSomeString:(NSString*)someString {// 這里也可以優化成異步派發 dispatch_asyncdispatch_sync(_syncQueue, ^{_someString = someString;}); }復制代碼

將同步與異步派發派發結合起來,可以實現與普通加鎖機制一樣的同步行為,這么做卻不會阻塞執行異步派發的的線程

并發隊列

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//讀取字符串 - (NSString*)someString {__block NSString *localSomeString;dispatch_sync(_syncQueue, ^{localSomeString = _someString;});return localSomeString; }//設置字符串 - (void)setSomeString:(NSString*)someString {// 柵欄函數dispatch_barrier_async(_syncQueue, ^{_someString = someString;}); }復制代碼

同步隊列及柵欄塊,可以零同步行文更加高效

Swift中的應用

同步鎖

Swift中沒有*@synchronized* ,但是可以使用objc_sync_enter,這是@synchronized的底層實現

func synced(_ lock: Any, closure: () -> ()) {objc_sync_enter(lock)closure()objc_sync_exit(lock) }// 使用 synced(self) {println("This is a synchronized closure") }復制代碼

也可以這樣進行分鐘:

// 也可以這樣使用 { objc_sync_enter(lock)defer { objc_sync_exit(lock) }//// code of critical section goes here// } // <-- 退出此塊時釋放鎖定// 如果我們加上異常的話,stackoverflow中有一個我覺得寫的比較好的方法: extension NSObject {func synchronized<T>(lockObj: AnyObject!, closure: () throws -> T) rethrows -> T{objc_sync_enter(lockObj)defer {objc_sync_exit(lockObj)}return try closure()} } 復制代碼

使用效果如下:

class Foo: NSObject {func test() {print("1")objc_sync_enter(self)defer {objc_sync_exit(self)print("3")}print("2")} }class Foo2: Foo {override func test() {super.test()print("11")objc_sync_enter(self)defer {print("33")objc_sync_exit(self)}print("22")} }let test = Foo2() test.test() 復制代碼

打印結果如下

1 2 3 11 22 33 復制代碼

信號量

static private var syncSemaphores: [String: DispatchSemaphore] = [:]static func synced(_ lock: String, closure: () -> ()) {//get the semaphore or create itvar semaphore = syncSemaphores[lock]if semaphore == nil {semaphore = DispatchSemaphore(value: 1)syncSemaphores[lock] = semaphore}//lock semaphoresemaphore!.wait()//execute closureclosure()//unlock semaphoresemaphore!.signal()} 復制代碼

并發隊列

方法與OC中是一樣的,在取值操作與賦值操作

public class UserData {private let myPropertyQueue = dispatch_queue_create("com.example.mygreatapp.property", DISPATCH_QUEUE_CONCURRENT)private var _myProperty = "" // Backing storagepublic var myProperty: String {get {var result = ""dispatch_sync(myPropertyQueue) {result = self._myProperty}return result}set {dispatch_barrier_async(myPropertyQueue) {self._myProperty = newValue}}} } 復制代碼

總結

以上是生活随笔為你收集整理的001@多用派发队列,少用同步锁的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。