[英]How to test thread-safety with XCTest
假設我們有一個具有可變狀態的以下類:
class Machine {
var state = 0
}
現在,讓我們說有一些內部機制來控制狀態。 但是,狀態更改可能發生在任何線程或隊列上,因此必須在線程安全環境中執行對state
屬性的讀取和寫入操作。 為此,我們將在dispatch_queue_t
上使用簡單的sync(:_)
方法來同步對state
變量的訪問。 (不是唯一的方法,但這只是一個例子)
現在,我們可以使用dispatch_sync(_:)
方法創建一個包含狀態值的私有變量和另一個包含自定義setter和getter的公共變量。
class Machine {
private var internalState = 0
var state: Int {
get {
var value: Int?
dispatch_sync(dispatch_get_main_queue()) {
value = self.internalState
}
return value!
}
set(newState) {
dispatch_sync(dispatch_get_main_queue()) {
self.internalState = newState
}
}
}
}
state
現在具有來自任何隊列或線程的安全同步訪問 - 它是線程安全的。
現在問題就在這里。
XCTest
測試此行為? 由於類Machine
可以有一個復雜的狀態機,我們需要測試它在任何環境中的表現:
state
訪問 state
成功測試此類行為的最佳方法是什么?
目前,我正在創建自定義調度隊列數組和已定義狀態數組。 然后我使用dispatch_async
方法來更改狀態並測試其值。 這引入了XCTest
執行的新問題,因為我需要跟蹤所有狀態突變何時完成。 該解決方案似乎相當復雜且難以維護。
為了實現更好的測試,我能做些什么?
在考慮測試這樣的線程安全代碼時,有兩個重要的移動部分:
雖然第一個可以通過使用模擬技術相對可測試,但后者難以測試,主要是因為驗證某些代碼是線程安全的,涉及單元測試來自多個線程的代碼同時訪問線程安全資源。 甚至這種技術也不是防彈,因為我們不能完全控制我們從單元測試中創建的線程的執行順序,也不能完全控制每個線程的分配時間,以確保我們捕獲可能發生的所有可能的競爭條件。
考慮到上述情況,我建議編寫一個提供鎖定機制的小類/結構,並在state
訪問器中使用它。 分離這樣的責任使得通過代碼審查更容易評估鎖定機制的正確性。
因此,我的建議是將線程安全代碼移動到專用包裝器中,並使用Machine
類中的包裝器:
/// A struct that just wraps a value and access it in a thread safe manner
public struct ThreadSafeBox<T> {
private var _value: T
/// Thread safe value, uses the main thread to synchronize the accesses
public var value: T {
get {
if Thread.isMainThread { return _value }
else { return DispatchQueue.main.sync { _value } }
}
set {
if Thread.isMainThread { _value = newValue }
else { DispatchQueue.main.sync { _value = newValue } }
}
}
/// Initializes the box with the given value
init(_ value: T) {
_value = value
}
}
ThreadSafeBox
代碼相對較小,並且在代碼審查時可以發現任何設計缺陷,因此理論上它的線程安全性可以通過代碼分析來證明。 一旦我們證明了ThreadSafeBox
的可靠性,我們就可以保證Machine
在其state
屬性方面也是線程安全的。
如果你真的想測試屬性訪問器,你可以驗證get / set操作只在主線程上運行的事實,這應該足以驗證線程的安全性。 請注意,鎖定機制與該類的實現細節有關,單元測試實現細節的缺點是緊密耦合單元和單元測試。 如果實現細節發生變化,這可能導致需要更新測試,這使得測試不太可靠。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.