Swift Array copy 的线程安全问题
Swift Array copy 的线程安全问题
NSArray 继承自 NSObject,属于对象,有 copy 方法。Swift 的 Array 是 struct,没有 copy 方法。把一个 Array 变量赋值给另一个变量,两个变量的内存地址相同吗?与此相关的有多线程安全问题。本文探究这两个问题。
内存地址
定义测试 class 和 struct
class MyClass {
var intArr = [Int]()
var structArr = [MyStructElement]()
var objectArr = [MyClassElement]()
}
struct MyStructElement {}
class MyClassElement {}
定义输出内存地址的 closure
let memoryAddress: (Any) -> String = {
guard let cVarArg = $0 as? CVarArg else { return "Can not find memory address" }
return String(format: "%p", cVarArg)
}
测试 Int array
private func testIntArr() {
print(#function)
let my = MyClass()
for i in 0...10000 {
my.intArr.append(i)
}
print("Before arr address:", memoryAddress(my.intArr))
// Copy Array is NOT thread safe
let arr = my.intArr // If move this into async closure, crash
print("Temp arr address:", memoryAddress(arr)) // Copy. Address different from my.intArr
DispatchQueue.global().async {
var sum = 0
for i in arr {
sum += i
}
print("Sum:", sum) // 0 + 1 + ... + 10000 = 50005000
}
my.intArr.removeAll()
for _ in 0...10000 {
my.intArr.append(0)
}
print("After arr address:", memoryAddress(my.intArr)) // New address
}
在 view controller 中进行测试
override func viewDidLoad() {
super.viewDidLoad()
for _ in 0...1000 {
testIntArr()
}
}
结果

Int array 的内存地址不同,赋值过程发生了 copy。
测试 struct array
private func testStructArr() {
print(#function)
let my = MyClass()
for _ in 0...10000 {
my.structArr.append(MyStructElement())
}
print("Before arr address:", memoryAddress(my.structArr))
// Copy Array is NOT thread safe
let arr = my.structArr // If move this into async closure, crash
print("Temp arr address:", memoryAddress(arr)) // Copy. Address different from my.structArr
DispatchQueue.global().async {
var sum = 0
for _ in arr {
sum += 1
}
print("Sum:", sum) // 10001
}
my.structArr.removeAll()
for _ in 0...10000 {
my.structArr.append(MyStructElement())
}
print("After arr address:", memoryAddress(my.structArr)) // New address
}
在 view controller 中进行测试
override func viewDidLoad() {
super.viewDidLoad()
for _ in 0...1000 {
testStructArr()
}
}
结果

Struct array 的内存地址不同,赋值过程发生了 copy。
测试 Object array
private func testObjectArr() {
print(#function)
let my = MyClass()
for _ in 0...10000 {
my.objectArr.append(MyClassElement())
}
print("Before arr address:", memoryAddress(my.objectArr))
// Copy Array is NOT thread safe
let arr = my.objectArr // If move this into async closure, crash
print("Temp arr address:", memoryAddress(arr)) // Not copy. Same as my.objectArr
DispatchQueue.global().async {
var sum = 0
for _ in arr {
sum += 1
}
print("Sum:", sum) // 10001
}
my.objectArr.removeAll()
for _ in 0...10000 {
my.objectArr.append(MyClassElement())
}
print("After arr address:", memoryAddress(my.objectArr)) // New address
}
在 view controller 中进行测试
override func viewDidLoad() {
super.viewDidLoad()
for _ in 0...1000 {
testObjectArr()
}
}
结果

一个 object array 变量赋值给另一个变量,两个变量的内存地址相同,也就是说没有 copy。原来的 array 改变后,内存地址改变,但不影响被赋值的变量。
线程安全问题
以上的写法是不会报错的。如果把 array 的赋值写入 async closure,就会报错。多试几次,会有不同的错误。
Int array 的错误
DispatchQueue.global().async {
let arr = my.intArr // 在这里赋值会报错
var sum = 0
for i in arr {
sum += i
}
print("Sum:", sum)
}



Struct array 的错误
DispatchQueue.global().async {
let arr = my.structArr // 在这里赋值会报错
var sum = 0
for _ in arr {
sum += 1
}
print("Sum:", sum)
}


Object array 的错误
DispatchQueue.global().async {
let arr = my.objectArr // 在这里赋值会报错
var sum = 0
for _ in arr {
sum += 1
}
print("Sum:", sum)
}


对于 Int array 和 struct array 来说,赋值时进行了 copy,但这个步骤应该不是原子操作,所以放入 async closure 会出错。对于 object array 来说,赋值过程虽然没有进行 copy,但是要改变原来的 array 并且保持被赋值的对象不变,应该也要进行 copy;也就是说在更新 array 时才进行 copy。推测此时的 copy 也不是原子操作,所以放入 async closure 会出错。
Array 的赋值过程是否进行 copy,与其中的元素类型有关。如果 array 的元素是 Int、struct 等,在赋值时就 copy。如果 array 的元素是 object,在赋值时不 copy,赋值后在更新其中一个 array 变量时才 copy。Array 的 copy 是线程不安全的。
转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/6288208.html
Swift Array copy 的线程安全问题的更多相关文章
- 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字
一. 各类数据结构比较及其线程安全问题 1. Array(数组): 分配在连续内存中,不能随意扩展,数组中数值类型必须是一致的.数组的声明有两种形式:直接定义长度,然后赋值:直接赋值. 缺点:插入数据 ...
- 【C#复习总结】探究各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字
前言 先普及一下线程安全和类型安全 线程安全: 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的 ...
- ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借
ASP.NET MVC深入浅出系列(持续更新) 一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...
- .NET面试题系列(五)数据结构(Array、List、Queue、Stack)及线程安全问题
常用数据结构的时间复杂度 如何选择数据结构 Array (T[]) 当元素的数量是固定的,并且需要使用下标时. Linked list (LinkedList<T>) 当元素需要能够在列表 ...
- iOS开发线程安全问题
先来看一下代码: - (void)viewDidLoad { [super viewDidLoad]; self.testStr = @"String initial complete&qu ...
- C# 多线程之List的线程安全问题
网上关于List的线程安全问题将的很少,所以自己实验了一把,发现确实是线程不安全的.所以当你在进行多线程编程中使用了共享的List集合,必须对其进行线程安全处理. List的Add方法是线程不安全的, ...
- 关于 SimpleDateFormat 的非线程安全问题及其解决方案
一直以来都是直接用SimpleDateFormat开发的,没想着考虑线程安全的问题,特记录下来(摘抄的): 1.问题: 先来看一段可能引起错误的代码: package test.date; impor ...
- Core Data 的线程安全问题
前言: 很多小的App只需要一个ManagedContext在主线程就可以了,但是有时候对于CoreData的操作要耗时很久的,比如App开启的时候要载入大量数据,如果都放在主线程,毫无疑问会阻塞UI ...
- Java 线程安全问题的本质
原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94 目录: 线程安全问题的本质 理解CPU JVM虚拟机类比于操作系统 重排序 汇总 一些解释 ...
随机推荐
- MySQL replace into 说明(insert into 增强版)
MySQL replace into 说明(insert into 增强版) 在插入数据到一个表时,通常是这种情况:1. 先推断数据是否存在: 2. 假设不存在,则插入:3.假设存在,则更新. 在 S ...
- CodeIgniter框架文件结构
转自网络:http://my.oschina.net/scholer/blog/99226 这个本来是很基础的东西,基本上用过CI的人都知道这些,原本是不消说的~但是因为毕业论文是关于CodeIgni ...
- jquery背景动画插件使用
在网页制作动画特效的时候,有时候想通过背景插入图片,然后通过控制背景显示的位置来实现一些动画效果,这样就不用使用绝对定位控制left和top来实现动画效果!但是jquery本身的动画函数是不支持背景动 ...
- cocos2d的-X- luaproject的LUA脚本加密
2014/1/26 更新 近期又发现了一个非常easy的方法,事实上coco2dx已经给我们提供设置loader的方法. 注意:有个局限性,在非android平台下调用pEngine->exec ...
- python购物淫秽数据分析(2)
淘宝大数据的游戏,我重新提高自己的思维方式, 插件和代码前前后后写在六个版本,但最好的结果其实是我的第一次2第二码.这让我很惊讶, 但它也说明了一个问题.当你更熟悉的语言,当一方,你缺少的是其他的知识 ...
- WebService它CXF这三个音符(Service接口实现类)
ITeacherServiceImpl.java: /** * @Title:ITeacherServiceImpl.java * @Package:com.you.service.impl * @D ...
- RabbitMq install on Centos6.3
安装服务(root) step 1: 启用EPEL:EPEL是一个Fedora Project 推出的 EPEL(Extra Packages for Enterprise Linux),EPEL是 ...
- ASP.NET MVC应用程序处理并发
为ASP.NET MVC应用程序处理并发 2014-05-14 08:37 by Bce, 694 阅读, 2 评论, 收藏, 编辑 这是微软官方教程Getting Started with Enti ...
- GridView使用技巧
http://yushuir.blog.163.com/blog/static/4346713820081023103937681/
- SignalR的服务器广播
可以试试 https://github.com/angular-ui/bootstrap 这个框架啊 [渣译文] SignalR 2.0 系列:SignalR的服务器广播 2014-03-13 09: ...