Design Patterns in Swift:单例模式(中)
来源:互联网 发布:矩阵分析与计算 pdf 编辑:程序博客网 时间:2024/06/06 03:06
转自:http://115.159.192.200/2016/01/14/design-patterns-in-swift-dan-li-mo-shi-zhong/
如有侵犯,请来信oiken@qq.com
这里的阐述是基于Design Patterns in Swift:单例模式(上),当单例模式与多线程碰头,两者应该如何愉快的合作呢?
处理并发情况
如果你正在一个多线程的环境下使用Singleton
,那么你需要去考虑不同模块同一时间去操作Singleton
会带来什么样的后果以及如果应对可能出现的问题。
潜在的多线程问题在项目中经常见到,包括单例模式(上)中涉及到的Logger
和backupServer
类都存在线程安全问题。因为他们涉及对数组进行操作,数组在Swift中是线程不安全的,Swift数组的扩容会重新创建一个新的数组(意味着地址完全不同)。然后ARC会为我们自动释放旧的数组,但这时候可能另一个线程还在访问旧的数组对象,极有可能crash
(如果没有是你运气太好)
并发操作Singleton
在上一节的代码基础上进行修改
import Foundationvar server = BackupServer.server;let queue = dispatch_queue_create("workQueue", DISPATCH_QUEUE_CONCURRENT);let group = dispatch_group_create();for count in 0 ..< 100 { dispatch_group_async(group, queue, {() in BackupServer.server.backup(DataItem(type: DataItem.ItemType.Email, data: "bob@example.com")) }); }dispatch_group_wait(group, DISPATCH_TIME_FOREVER);println("\(server.getData().count) items were backed up");
这里是使用了GCD的方式来进行多线程操作,调用backup
方法100次。虽然对GCD有一些了解,但是非常碎片化,最近两天打算 总结一下GCD的用法,这了先简单介绍一下代码中用到的知识。
let queue = dispatch_queue_create("workQueue", DISPATCH_QUEUE_CONCURRENT);
代表创建一个名为workQueue
的并发队列
let group = dispatch_group_create();
代表创建一个GCD任务组
for count in 0 ..< 100 { dispatch_group_async(group, queue, {() in BackupServer.server.backup(DataItem(type: DataItem.ItemType.Email, data: "bob@example.com")) }); }
把队列queue放到调度组中以异步的方式执行,代表100个闭包的内容可以同时执行,不会因为其他block而阻塞。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
代表会等待到group中的所有任务都完成再去执行下面的代码。
我们运行一下程序,发现crash
掉了,并且debugger
在data.append()
方法处中断(break),上面说到了数组扩容的原理,对于线程不安全的操作导致crash
(错误提示:manipulating the contents of a Swift array isn’t a thread-safe operation, and singletons that use arrays need concurrency protections.)
串行操作
为了解决这个问题,我需要确定在一个时间点只有一个block去调用backup
方法来对数组进行扩容。在这里采取了GCD的串行方式。
import Foundationclass DataItem { enum ItemType : String { case Email = "Email Address"; case Phone = "Telephone Number"; case Card = "Credit Card Number"; } var type:ItemType; var data:String; init(type:ItemType, data:String) { self.type = type; self.data = data; } }final class BackupServer { let name:String; private var data = [DataItem](); private let arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_SERIAL); private init(name:String) { self.name = name; globalLogger.log("Created new server \(name)");}func backup(item:DataItem) { dispatch_sync(arrayQ, {() in self.data.append(item); globalLogger.log("\(self.name) backed up item of type \(item.type.rawValue)"); })}func getData() -> [DataItem]{ return data;}class var server:BackupServer { struct SingletonWrapper { static let singleton = BackupServer(name:"MainServer"); } return SingletonWrapper.singleton;}}
对部分代码进行分析
private let arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_SERIAL);
创建名为arrayQ
的串行队列
dispatch_sync(arrayQ, {() in self.data.append(item); globalLogger.log("\(self.name) backed up item of type \(item.type.rawValue)"); })
同步访问data.append()
,每一个时间点只有一个线程在操作数组。
我们上一节主要解决了BackupServer
和Logger
两个类的单例问题,同样这里,也需要对两个类都进行线程安全的保护,虽然在BackupServer
中我们是同步的调用globalLogger.log()
方法,但是可能在其他模块也调用了globalLogger.log()
方法,那么我们就需要在Logger
类中进行修改保护。
import Foundation;let globalLogger = Logger();final class Logger { private var data = [String]() private let arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_SERIAL); private init() { // do nothing - required to stop instances being // created by code in other files } func log(msg:String) { dispatch_sync(arrayQ, {() in self.data.append(msg); }); }func printLog() { for msg in data { println("Log: \(msg)"); }} }
这里也使用相同的方法创建串行队列,同步方法确认一个时间点只有一个线程对数组进行修改操作。
这时候运行我们的程序正常结果被打印
100 items were backed up
如果叙述存在问题,请联系我,谢谢
- Design Patterns in Swift:单例模式(中)
- Design Patterns in Swift:单例模式(上)
- Design Patterns in Android:单例模式
- Design Patterns: Singleton Basics 设计模式游戏中运用:单例基础
- 设计模式之单例模式 (Design patterns of the The singleton pattern)c#
- 单例模式——Head First Design Patterns
- 《Head First Design Patterns》读书笔记之单例模式
- Design patterns of Singleton(设计模式之单例)
- Swift 模式(Patterns)
- Design Patterns in Android:策略模式
- Design Patterns in Android:模板方法模式
- 设计模式(Design Patterns)
- 设计模式(Design Patterns)
- 设计模式(Design Patterns)
- 设计模式(Design Patterns)
- 设计模式(Design Patterns)
- 设计模式(Design Patterns)
- 设计模式(Design Patterns)
- 微服务、SOA 和 API对比与分析
- WDCP_PHP降级至5.2.17
- loadRunner12 设置关联 获取服务端动态数据
- Linux下g++编译与使用静态库和动态库
- windows GUI开发01 - 显示"Hello,world"
- Design Patterns in Swift:单例模式(中)
- Linux内核---16.启动分析4uboot与内核的参数传递
- Map
- Linux内核---17.platform_device的添加过程
- Java中的constructor
- 程序中中文乱码问题的总结
- PostgresQL FDW 源码分析之 postgresGetForeignPaths()
- Java单例的正确使用姿势
- LCIS