構建乙個 synchronized

2021-09-11 10:01:42 字數 2878 閱讀 2901

校對:智多芯

定稿:numbbbbb,cmb

@synchronized在 objective-c 中是一種控制結構。它接受乙個物件指標作為引數,後面跟著一段**塊。物件指標充當鎖,在任何時候@synchronized**塊中只允許有乙個執行緒使用該物件指標。

這是一種使用鎖進行多執行緒程式設計的簡單方法。舉個例子,你可以使用nslock來保護對 nsmutablearray 的操作:

nsmutablearray *array;

nslock *arraylock;

[arraylock lock];

[array addobject: obj];

[arraylock unlock];

複製**

也可以使用@synchronized來將陣列本身加鎖:

@synchronized(array) 

複製**

swift 版本的@synchronized是乙個函式。它接受乙個物件和乙個閉包,並使用持有的鎖呼叫閉包:

func

synchronized

(obj: anyobject, f: void -> void)

複製**

問題是,如何將任意物件變成鎖?

在乙個理想的世界裡(從實現這個函式的角度來看),每個物件都會為鎖留出一些額外空間。在這個額外的小空間裡synchronized可以使用適當的lockunlock方法。然而實際上並沒有這種額外空間。這可能是件好事,因為這會增大物件占用的記憶體空間,但是大多數物件永遠都不會用到這個特性。

另一種方法是用一張表來記錄物件到鎖的對映。synchronized可以查詢表中的鎖,然後執行lockunlock操作。這種方法的問題是表本身需要保證執行緒安全,它要麼需要自己的鎖,要麼需要某種特殊的無鎖資料結構。為表單獨設定乙個鎖要容易得多。

為了防止鎖不斷累積常駐,表需要跟蹤鎖的使用情況,並在不再需要鎖的時候銷毀或者復用。

要實現將物件對映到鎖的表,nsmaptable非常合適。它可以把原始物件的位址設定成鍵(key),並且可以儲存對鍵(key)和值(value)的弱引用,從而允許系統自動**未被使用的鎖。

let lockstable = nsmaptable.weaktoweakobjectsmaptable()

複製**

表中儲存的物件是nsrecursivelock例項。因為它是乙個類,所以可以直接用在nsmaptable中,這點pthread_mutex_t就做不到。@synchronized支援遞迴語義,我們的實現一樣支援。

表本身也需要乙個鎖。自旋鎖(spinlock)在這種情況下很適合使用,因為對錶的訪問是短暫的:

var lockstablelock = os_spinlock_init

複製**

有了這個表,我們就可以實現以下方法:

func

synchronized

(obj: anyobject, f: void -> void)

複製**

有了鎖之後主表鎖就可以釋放了。為了避免死鎖這必須要在呼叫f之前完成:

osspinlockunlock(&lockstablelock)

複製**

現在我們可以呼叫f了,在呼叫前後分別進行加鎖和解鎖操作:

lock!.lock()

f()lock!.unlock()

}複製**

蘋果實現@synchronized的方案可以在 objective-c runtime 原始碼中找到:

它的主要目標是效能,因此不像上面那個玩具般的例子那麼簡單。對比它們之間有什麼異同是一件非常有趣的事。

基本概念是相同的。存在乙個全域性表,它將物件指標對映到鎖,然後該鎖在@synchronized**塊前後進行加鎖解鎖操作。

表本身的實現是乙個鍊錶而不是乙個雜湊表。常見的情況是在任何給定的時間裡只存在少數幾個鎖,所以鍊錶的效能表現很不錯,可能比雜湊表效能更好。每個執行緒快取了最近在當前執行緒查詢的鎖,從而進一步提高效能。

蘋果的實現並不是只有乙個全域性表,而是在乙個陣列裡儲存了 16 個表。物件根據位址對映到不同的表,這減少了不同物件@synchronized操作導致的不必要的資源競爭,因為它們很可能使用的是兩個不同的全域性表。

蘋果的實現沒有使用弱指標引用(這會大量增加額外開銷),而是為每個鎖保留乙個內部的引用計數。當引用計數達到零時,該鎖可以給新物件重新使用。未使用的鎖不會被銷毀,但復用意味著在任何時間鎖的總數都不能超過啟用鎖的數量,也就是說鎖的數量不隨著新物件的建立無限制增長。

蘋果的實現方案非常巧妙,效能也不錯。但與使用單獨的顯式鎖相比,它仍然會帶來一些不可避免的額外開銷。尤其是:

如果不相關的物件剛好被分配到同乙個全域性表中,那麼它們仍然可能存在資源競爭。

必須做更多的工作來查詢全域性表中物件的鎖。

即使不需要,每個加鎖/解鎖週期都會產生遞迴語義方面的開銷。

@synchronized是乙個有趣的語言結構,實現起來並不簡單。它的作用是實現執行緒安全,但它的實現本身也需要同步操作來保證執行緒安全。我們使用全域性鎖來保護對鎖表的訪問,蘋果的實現中則使用不同的技巧來提高效能。

構建乙個學習演算法

構建乙個學習演算法的推薦方法 1.先設計乙個簡單快速實現的演算法,實現該演算法並通過交叉驗證集測試該演算法 2.繪製學習曲線,通過觀察學習曲線,判斷模型是欠擬合還是過擬合,然後決定通過增加測試集,增加特徵,或者其他有效方法 3.進行誤差分析,人工檢查交叉驗證集中演算法判斷錯誤的例項,看看這些例項是否...

Django 構建乙個專案

一 建立django程式 終端命令 django admin startproject fahaicmd ide建立django程式時,本質上都是自動執行上述命令 其他常用命令 python manage.py createsuperuser 使用命令列建立預設超級使用者 二 配置檔案 django...

Django速成 構建乙個Blog Demo

根據書本引導構建的乙個django blog demo,還很簡陋。主要為引導使用者熟悉django的流程,以及運作原理。python版本 3.5.2,django版本 1,9,7,final 0 配置完是資料庫後,首先在manage.py統計目錄下執行下列兩個命令使資料庫生效。manage.py m...