Swift百萬線程攻破單例(Singleton)模式

1、不安全的單例實現

在上一篇文章咱們給出了單例的設計模式,直接給出了線程安全的實現方法。單例的實現有多種方法,以下面: javascript


class SwiftSingleton {
    class var shared: SwiftSingleton {
    if !Inner.instance {
        Inner.instance = SwiftSingleton()
        }
        return Inner.instance!
    }
    
    struct Inner {
        static var instance: SwiftSingleton?
    }
}
這段代碼的實現,在shared中進行條件判斷,若是Inner.instance.爲空就生成一個實例,這段代碼很簡單看出當線程同時訪問SwiftSingleton.shared方法時,會有以下問題出現,線程A判斷Inner.instance爲空,進入if語句後當即切換到線程B執行,線程B也進行判斷,因爲線程A只是進入了if語句,這行代碼
Inner.instance = SwiftSingleton()
並無執行,這時Inner.instance仍是爲空,純種B也進行了if語句,這種狀況下就會建立多個實例,沒有保證明例的惟一性。上面的理論分析基本上任何一篇文章都會講的,也不能理解,關鍵問題,如何測試上面的理論是否正確呢?

2、線程搶佔原理

其實要實現上面的例子不是很難,建立N個線程,讓他同時訪問SwiftSingleton.shared的方法,而後將所返回值保存最後比較引用。原理很正確,可是建立線程的過程也是極爲耗時的,如今的電腦執行速度又很是快,模擬具備不穩定性。如何才能最大的程序測試上面的安全性呢?這裏咱們能夠考慮一個現實的問題,假設找1000人經過一段100米的賽道,咱們想要更多的人同時去衝刺終點,越多越好。若是你找一我的,告訴他去跑100米,而後再找一下,這種確定同時到達終點的概率很底。怎麼辦才能讓更多的人在同一時刻到達終點呢?問題很簡單,讓這1000人有一個同一塊兒跑點,讓他們都準備好了,隨着一聲令下,一塊兒奔跑。回到技術問題,咱們想要更多的線程訪問SwiftSingleton.shared方法,只要先準備好全部的線程,而後發一個信號,讓他們同時去訪問這個方法就能夠了。 java


實現代碼以下: 設計模式

class SwiftSingletonTest: XCTestCase {
    let condition = NSCondition()
    let mainCondition = NSCondition()
    let singleton: NSMutableArray = NSMutableArray()
    let threadNumbers = 1000
    var count = 0
    
    
    func testSingletonThreadSafe() {
        
        for index in 0...threadNumbers {
            NSThread.detachNewThreadSelector("startNewThread", toTarget: self, withObject: nil)
        }
        condition.broadcast()
        mainCondition.lock()
        mainCondition.wait()
        mainCondition.unlock()
        checkOnlyOne()
    }
    
    func startNewThread() {
        condition.lock()
        condition.wait()
        condition.unlock()
        let temp = SwiftSingleton.shared
        count++
        singleton.addObject(temp)
        if count >= threadNumbers {
            mainCondition.signal()
        }
    }
    
    func checkOnlyOne () {
        let one = singleton[0] as SwiftSingleton
        for temp : AnyObject  in singleton {
            let newTemp = temp as SwiftSingleton
            if(newTemp !== one) {
                XCTFail("singleton error!");
                break;
            }
        }
    }

}


這段代碼主要使用了NSCondition進行同步,其中NSCondition分爲兩組,condition主要負責除主線程外的線程,在for語句中會建立並啓動N(threadNumbers)個線程,每一個線程啓動後都會去執行startNewThread方法,執行到語句 安全

condition.wait()
會掛起當前線程,當全部線程都建立並啓動完時,主線程會執行
condition.broadcast()
來通知掛起的N個線程繼承執行,此時主線程調了
mainCondition.wait()
主線和進入持起狀態,此處將主線程掛起是爲了在全部線程執行完,依次檢查取得引用的惟一性。
if count >= threadNumbers {
            mainCondition.signal()
}
當全部線程執行完時,通知主線程開始檢查引用 ,執行結果以下:


從上面執行結果能夠看出,這種單例並不能保證惟一性。上面用到了NSMutableArray類,網上說是線程不安全的,這裏用的Swift語言,這麼多線程一塊兒操做暫沒有發現異常...... 多線程


3、其它實現測試結果

一、最簡單實現
class SwiftSingleton {
    class var shared: SwiftSingleton {
            return Inner.instance
    }

    struct Inner {
        static let instance: SwiftSingleton = SwiftSingleton()
    }
}

解釋:上述表明也實現了延遲加載技術
static let instance: SwiftSingleton = SwiftSingleton()
首次訪問Inner.instance時纔會建立SwiftSingleton,此處的延遲加載由Swift語言原生提供


測試結果:經過


二、使用GCD技術實現的單例模式
class SwiftSingleton {
    class var shared: SwiftSingleton {
        dispatch_once(&Inner.token) {
            Inner.instance = SwiftSingleton()
        }
        return Inner.instance!
    }
    struct Inner {
        static var instance: SwiftSingleton?
        static var token: dispatch_once_t = 0
    }
    
}

測試結果:經過

4、測試說明

一、Mac OS線程總量有限制,你能夠建立線程,可是最大線程啓動數爲2048(個人電腦是這樣,不清楚是否跟硬件有關)
二、若是遇到測試無響應時,能夠嘗試重啓電腦
相關文章
相關標籤/搜索