提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
原创|使用教程|编辑:黄竹雯|2016-08-29 14:52:48.000|阅读 771 次
概述:一起来了解新的调试工具,这会将你从iOS 10应用程序开发的线程“地狱”中解救出来。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
相关链接:
通常,这些是由于多个线程同时访问相同的一些内存而造成的。我猜想,线程问题是许多开发人员做噩梦的原因。他们是出了名的难以追踪,错误只发生在特定条件下:所以确定问题的根源是非常复杂的。
通常导致线程问题的原因是所谓的“竞争条件”。我们不会去关注太多的细节,像是这意味着什么,而是从谷歌引用ThreadSanitizer手册:
这些用来追踪的是一个绝对的噩梦,但值得庆幸的是Xcode附带一个新的调试工具叫做Thread Sanitizer,甚至可以在你注意到他们之前帮助识别这些问题。
我们将创建一个简单的应用程序,使我们能够存款和取款100美元面额。像往常一样,项目的完成版本已在GitHub上(为了方便各位读者,小编已经为大家整理了,请点击这里下载)。
我们的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方法已经几乎立即执行,然而,withdraw还需要一段时间才能完成。我们会说这是因为我们需要为取款执行一些欺诈检查,但实际上我们只发送当前线程睡眠2秒。这将给我们后面使用一些多线程提供借口。
唯一需要注意的另一件事是完成模块,这是当存款和取款都成功完成时才执行。
我们的视图控制器由两个按钮——存款和取款,以及一个显示当前余额的标签组成。故事板的布局:
为连接我们的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)" } }
让我们给它一个旋转:
嗯……当我们试着取回钱时有点慢!这是由于我们Account 的withdraw方法及其严格的“欺诈检查”,导致主线程阻塞,直到该方法已经完成。我们希望用户能够以最小的延迟反复点击“Deposit”和“Withdraw”。
如果我们可以从主线程删除阻塞的withdraw方法,这就太棒了。我们将使用新“Swiftified”中央调度库:
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() } } }
让我们再次运行它:
等一等!我们的钱哪里去了?我们存入100美元,取回了100美元然后,剩下0,尽管开始时是100美元!
我们有信心我们的方法按预期的运行(因为他们是单元测试),它看起来就像我们的withdraw任务调度到背景队列引发了一个问题。
打开检查工具非常简单,只需将你的目标的计划设置和在Diagnostics标签中检查Thread Sanitizer箱。我们可以选择在遇到的问题上暂停,这使得它能够容易地在个案基础上评估每一个问题。我们会这样。
由于线程检查工具只在运行时起作用,我们需要重新编译和重新运行应用程序。让我们开始吧。
当我们重复我们之前取款的过程,然后立即存款,线程检查工具会暂停我们的应用程序的执行,因为它发现了竞态条件。这给了我们一个很好的冲突访问发生的地方的堆栈跟踪。
它还将结果输出到控制台,所以你没有必要从Xcode运行检查工具。
通过堆栈跟踪和提供的信息,线程分析仪有助于表明,当访问Account.balance属性时在我们的Account.deposit和Account.withdraw方法中有一个数据竞争。哦,看来我们需要在withdraw和deposit方法中使用相同的串行调度队列:
我们将修改我们的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() } } } }
再次运行应用程序显示了我们仍然有数据竞争,但是它不再是在我们的Account类中,而是由于我们的ViewController从主线程访问balance。
我们可以通过转换到一个只有访问Account的私有变量来保护我们的balance属性,而不是用我们的队列返回balance。
private var _balance: Int = 0 var balance: Int { return queue.sync { return _balance } }
我们需要转换任何书面到平衡变量以使用私有_balance属性。
现在当我们运行我们的应用程序,我们应该能够多次点击“withdraw”和“deposit”而无需令人不安的线程检查工具。太好了,我们刚刚使用这个新工具来修正了我们的错误代码。
虽然看起来似乎不像起初那样,线程检查工具可能会成为开发人员工具箱中一个非常重要的iOS工具。它发现数据竞争的能力,即使在程序的运行期间没有发生,也可能会拯救无数小时调试断断续续的线程问题的时间。
像往常一样,苹果的WWDC大会很丰富,值得一看。sanitizer是Clang编译器的一部分,在LLVM网站上可以找到更详细的信息,在谷歌建立了sanitizer的团队有许多有趣的wiki页面,其中包括了算法用于检测线程问题的高层次的演练。
本文翻译自:
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@capbkgr.cn
本文探讨 SQL Server 中 NULL 和空值之间的区别,并讨论如何有效地处理它们。
Unity 是一款功能极其丰富的游戏引擎,允许开发人员将各种媒体集成到他们的项目中。但是,它缺少最令人兴奋的功能之一 - 将 Web 内容(例如 HTML、CSS 和 JavaScript)直接渲染到 3D 场景中的纹理上的能力。在本文中,我们将介绍如何使用 DotNetBrowser 在 Unity3D 中将 Web 内容渲染为纹理。
DevExpress v24.2帮助文档正式发布上线了,请按版本按需下载~
本教程将向您展示如何用MyEclipse构建一个Web项目,欢迎下载最新版IDE体验!
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@capbkgr.cn
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢