簡體   English   中英

如何使用XCTest測試線程安全性

[英]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執行的新問題,因為我需要跟蹤所有狀態突變何時完成。 該解決方案似乎相當復雜且難以維護。

為了實現更好的測試,我能做些什么?

在考慮測試這樣的線程安全代碼時,有兩個重要的移動部分:

  1. 狀態訪問器僅在鎖定的上下文中運行
  2. 鎖定機制實際上是線程安全的。

雖然第一個可以通過使用模擬技術相對可測試,但后者難以測試,主要是因為驗證某些代碼是線程安全的,涉及單元測試來自多個線程的代碼同時訪問線程安全資源。 甚至這種技術也不是防彈,因為我們不能完全控制我們從單元測試中創建的線程的執行順序,也不能完全控制每個線程的分配時間,以確保我們捕獲可能發生的所有可能的競爭條件。

考慮到上述情況,我建議編寫一個提供鎖定機制的小類/結構,並在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM