• <menu id="w2i4a"></menu>
  • logo 移動開發(fā)學習指南
    文檔首頁>>移動開發(fā)學習指南>>iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具

    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具


    通常,這些是由于多個線程同時訪問相同的一些內(nèi)存而造成的。我猜想,線程問題是許多開發(fā)人員做噩夢的原因。他們是出了名的難以追蹤,錯誤只發(fā)生在特定條件下:所以確定問題的根源是非常復雜的。

    通常導致線程問題的原因是所謂的“競爭條件”。我們不會去關注太多的細節(jié),像是這意味著什么,而是從谷歌引用ThreadSanitizer手冊:


    數(shù)據(jù)競爭發(fā)生在當兩個線程同時訪問同一變量,并且至少有一個訪問是編寫狀態(tài)時。


    這些用來追蹤的是一個絕對的噩夢,但值得慶幸的是Xcode附帶一個新的調(diào)試工具叫做Thread Sanitizer,甚至可以在你注意到他們之前幫助識別這些問題。

    The Project

    我們將創(chuàng)建一個簡單的應用程序,使我們能夠存款和取款100美元面額。像往常一樣,項目的完成版本已在GitHub上(為了方便各位讀者,小編已經(jīng)為大家整理了,請點擊這里下載)。

    The Account

    我們的Account模式非常簡單:

    import Foundation
    class Account {
        var balance: Int = 0
        func withdraw(amount: Int, completed: () -> ()) {
            let newBalance = self.balance - amount
            if newBalance < 0 {
                print("You don't have enough money to withdraw \(amount)")
                return
            }
            // Simulate processing of fraud checks
            sleep(2)
            self.balance = newBalance
            completed()
        }
        func deposit(amount: Int, completed: () -> ()) {
            let newBalance = self.balance + amount
            self.balance = newBalance
            completed()
        }
    }

    它包含幾個方法使我們能夠取款和存款到我們的賬戶。存款和取款金額是硬編碼$100。

    deposit方法已經(jīng)幾乎立即執(zhí)行,然而,withdraw還需要一段時間才能完成。我們會說這是因為我們需要為取款執(zhí)行一些欺詐檢查,但實際上我們只發(fā)送當前線程睡眠2秒。這將給我們后面使用一些多線程提供借口。

    唯一需要注意的另一件事是完成模塊,這是當存款和取款都成功完成時才執(zhí)行。

    視圖控制器

    我們的視圖控制器由兩個按鈕——存款和取款,以及一個顯示當前余額的標簽組成。故事板的布局:

    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具

    為連接我們的UI元素,我們有一個IBOutlet,引用平衡標簽和以用戶當前的平衡更新標簽的方法。

    import UIKit
    class ViewController: UIViewController {
        @IBOutlet var balanceLabel: UILabel!
        let account = Account()
        override func viewDidLoad() {
            super.viewDidLoad()
            updateBalanceLabel()
        }
        @IBAction func withdraw(_ sender: UIButton) {
            self.account.withdraw(amount: 100, onSuccess: updateBalanceLabel)
        }
        @IBAction func deposit(_ sender: UIButton) {
            self.account.deposit(amount: 100, onSuccess: updateBalanceLabel)
        }
        func updateBalanceLabel() {
            balanceLabel.text = "Balance: $\(account.balance)"
        }
    }

    讓我們給它一個旋轉(zhuǎn):

    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具

    嗯……當我們試著取回錢時有點慢!這是由于我們Account 的withdraw方法及其嚴格的“欺詐檢查”,導致主線程阻塞,直到該方法已經(jīng)完成。我們希望用戶能夠以最小的延遲反復點擊“Deposit”和“Withdraw”。

    救援調(diào)度隊列

    如果我們可以從主線程刪除阻塞的withdraw方法,這就太棒了。我們將使用新“Swiftified”中央調(diào)度庫:

    func withdraw(amount: Int, onSuccess: () -> ()) {
        DispatchQueue(label: "com.shinobicontrols.balance-moderator").async {
            let newBalance = self.balance - amount
            if newBalance < 0 {
                print("You don't have enough money to withdraw \(amount)")
                return
            }
            // Simulate processing of fraud checks
            sleep(2)
            self.balance = newBalance
            DispatchQueue.main.async {
                onSuccess()
            }
        }
    }

    讓我們再次運行它:

    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具

    等一等!我們的錢哪里去了?我們存入100美元,取回了100美元然后,剩下0,盡管開始時是100美元!

    我們有信心我們的方法按預期的運行(因為他們是單元測試),它看起來就像我們的withdraw任務調(diào)度到背景隊列引發(fā)了一個問題。

    Thread Sanitizer線程檢查工具來拯救我們的理智!

    打開檢查工具非常簡單,只需將你的目標的計劃設置和在Diagnostics標簽中檢查Thread Sanitizer箱。我們可以選擇在遇到的問題上暫停,這使得它能夠容易地在個案基礎上評估每一個問題。我們會這樣。

    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具
    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具

    由于線程檢查工具只在運行時起作用,我們需要重新編譯和重新運行應用程序。讓我們開始吧。


    在WWDC上,蘋果建議在你所有的單元測試開啟線程檢查工具。檢查工具在運行時操作,如果代碼執(zhí)行,只能夠確定數(shù)據(jù)競爭。如果你的代碼完全得以單元測試,那么你可能會發(fā)現(xiàn)線程檢查工具發(fā)現(xiàn)了大多數(shù)問題,如果不是全部測試,發(fā)現(xiàn)的是你的項目的競態(tài)條件 (你會發(fā)現(xiàn)我們博客的iOS 9 Day by Day中一個有用的閱讀,Xcode 7的代碼覆蓋工具)。

    其他值得注意的是,它只能運行在語言版本3編寫的Swift代碼上(Objective-C也可兼容),并且只能使用64位模擬器運行。


    當我們重復我們之前取款的過程,然后立即存款,線程檢查工具會暫停我們的應用程序的執(zhí)行,因為它發(fā)現(xiàn)了競態(tài)條件。這給了我們一個很好的沖突訪問發(fā)生的地方的堆棧跟蹤。

    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具

    它還將結(jié)果輸出到控制臺,所以你沒有必要從Xcode運行檢查工具。

    通過堆棧跟蹤和提供的信息,線程分析儀有助于表明,當訪問Account.balance屬性時在我們的Account.deposit和Account.withdraw方法中有一個數(shù)據(jù)競爭。哦,看來我們需要在withdraw和deposit方法中使用相同的串行調(diào)度隊列:

    我們將修改我們的Account類來使用共享隊列:

    class Account {
        var balance: Int = 0
        private let queue = DispatchQueue(label: "com.shinobicontrols.balance-moderator")
        func withdraw(amount: Int, onSuccess: () -> ()) {
            queue.async {
                // Same as earlier...
            }
        }
        func deposit(amount: Int, onSuccess: () -> ()) {
            queue.async {
                let newBalance = self.balance + amount
                self.balance = newBalance
                DispatchQueue.main.async {
                    onSuccess()
                }
            }
        }
    }

    再次運行應用程序顯示了我們?nèi)匀挥袛?shù)據(jù)競爭,但是它不再是在我們的Account類中,而是由于我們的ViewController從主線程訪問balance。

    iOS開發(fā)文集iOS 10 Day By Day: Thread Sanitizer線程檢查工具

    我們可以通過轉(zhuǎn)換到一個只有訪問Account的私有變量來保護我們的balance屬性,而不是用我們的隊列返回balance。

    private var _balance: Int = 0
    var balance: Int {
        return queue.sync {
            return _balance
        }
    }

    我們需要轉(zhuǎn)換任何書面到平衡變量以使用私有_balance屬性。

    現(xiàn)在當我們運行我們的應用程序,我們應該能夠多次點擊“withdraw”和“deposit”而無需令人不安的線程檢查工具。太好了,我們剛剛使用這個新工具來修正了我們的錯誤代碼。

    進一步的閱讀

    雖然看起來似乎不像起初那樣,線程檢查工具可能會成為開發(fā)人員工具箱中一個非常重要的iOS工具。它發(fā)現(xiàn)數(shù)據(jù)競爭的能力,即使在程序的運行期間沒有發(fā)生,也可能會拯救無數(shù)小時調(diào)試斷斷續(xù)續(xù)的線程問題的時間。

    像往常一樣,蘋果的WWDC大會很豐富,值得一看。sanitizer是Clang編譯器的一部分,在LLVM網(wǎng)站上可以找到更詳細的信息,在谷歌建立了sanitizer的團隊有許多有趣的wiki頁面,其中包括了算法用于檢測線程問題的高層次的演練。


    我們使用Swift 3中提供給我們的一個小的新面貌GCD。蘋果也在“Concurrent Programming With GCD in Swift 3”談話中談到了這個,你可能會發(fā)現(xiàn)它的用處。此外,Roy Marmelstein寫了一篇很好且簡潔的帖子闡述這一變化。


    本文翻譯自:iOS 10 Day by Day: Thread Sanitizer

     

    PS: 關于移動開發(fā),這些產(chǎn)品你可以關注>>
    關于移動開發(fā)的最新資訊和產(chǎn)品推薦,請<咨詢在線客服>!
    掃碼咨詢


    添加微信 立即咨詢

    電話咨詢

    客服熱線
    023-68661681

    TOP
    三级成人熟女影院,欧美午夜成人精品视频,亚洲国产成人乱色在线观看,色中色成人论坛 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();