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虚拟机类比于操作系统 重排序 汇总 一些解释 ...
随机推荐
- [翻译]如何编写GIMP插件(一)
近期想尝试编写gimp插件,在gimp官网看到了三篇简明教程,顺便翻译了下,由于本人英文,计算机知识有限,文中难免有warning,error出现,欢迎指正. <How to write a G ...
- Android_WebServices_介绍
本博文为子墨原创,转载请注明出处! http://blog.csdn.net/zimo2013/article/details/38036289 1.WebService的介绍 WebService为 ...
- C# File类的操作
原文:C# File类的操作 File类,是一个静态类,主要是来提供一些函数库用的.静态实用类,提供了很多静态的方法,支持对文件的基本操作,包括创建,拷贝,移动,删除和打开一个文件.File类方法的参 ...
- 简单分析android textview xml 的属性设置
android:ems 设置TextView的宽度为N个字符的宽度. 这样的好处就是,在定义编辑框空间输入多少字符的时候,可以根据固定的值设置编辑框宽度.保证边框和文字的宽度统一.android:ma ...
- Linq无聊练习系列2--select/distinct练习
void dataBindByWhere() { /**************select/distinct 练习*******************/ //获 ...
- SQL Server中tempdb的management
对<SQL Server中tempdb的management>的一些更正和补充 对<SQL Server中tempdb的management>的一些更正和补充 前几天看了这 ...
- Paint.NET
http://www.dotpdn.com/downloads/pdn.html Paint.NET To download, please click the Free Download Now l ...
- KVC和KVO实现监听容器类(数组等)的变化
KVC,即Key-Value Coding,键值编码,简单地说,就是可以由key获取一个object对应的property.举个例子,如果一个对象object,它有一个属性item,你可以通过valu ...
- IE回车的怪异行为
问题 IE浏览器在input中回车,会触发button按钮的click事件. 原因分析 IE浏览器中在input中回车相当于提交form,而button标签默认的类型是submit, 所以如果没有指定 ...
- 圆形头像以及一些常见需求形状自定义ImageView组件
在实际开发中,我们经常会遇到这样的需求,就是无论图片长啥样,我们都要其显示成圆形.圆形加一个边框.矩形加边框,带圆角的矩形等等,lib和demo下载地址:https://github.com/mapl ...