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虚拟机类比于操作系统 重排序 汇总 一些解释 ...
随机推荐
- 曲演杂坛--一条DELETE引发的思考
原文:曲演杂坛--一条DELETE引发的思考 场景介绍: 我们有一张表,专门用来生成自增ID供业务使用,表结构如下: CREATE TABLE TB001 ( ID ,) PRIMARY KEY, D ...
- HDU 4630、BOJ 某题
两道离线线段树. 比赛时候没想到.... 扫描数组,i从1到n,线段树维护从1到i每一个约数(1~50000)的出现的最近位置,线段树存储的是约数的最大值 #include<cstdio> ...
- bootstrap错误警告信息提示
bootstrap提供了成功执行.警告和错误信息的样式. 在使用该功能的时候需要引入以下几个文件: bootstrap.css jquery.js(需放在bootstrap.js之前) bootstr ...
- Json.Net6.0
Json.Net6.0入门学习试水篇 前言 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.简单地说,JSON 可以将 JavaScript 对象中表 ...
- win7 64位系统装oracle11 提示环境变量path 值超过1023字符
win7 64位系统装oracle10 提示环境变量path 值超过1023字符 1.提示环境变量path 值超过1023字符. 方案: 1.oracle安装文件,右键属性--设置兼容性--兼容XP ...
- Asp.net MVC的Model Binder工作流程以及扩展方法(1)
Asp.net MVC的Model Binder工作流程以及扩展方法(1)2014-03-19 08:02 by JustRun, 523 阅读, 4 评论, 收藏, 编辑 在Asp.net MVC中 ...
- 了解你的被测系统(why?)
了解你的被测系统(why?) 如何做好系统集成测试[二.了解你的被测系统] 如果看完了第一篇文章,你的答案是Yes.我们可以继续讨论如何做系统集成测试啦. 了解你的被测系统(why?) 一如既 ...
- 定义 ICache 接口,以及实现默认的 ASP.NET 缓存机制
本文定义 ICache 接口,以及实现默认的 ASP.NET 缓存机制(即通过 System.Web.Caching.Cache)来缓存,将来也可以通过扩展,替换默认实现. 下面直接贴代码了: ICa ...
- 线程:Semaphore实现信号灯
Semaphore是一个计数的信号量,可以维护当前访问自身的线程个数,并提供了同步机制.使用Semaphore可以控制同时访问资源的线程个数,例如实现一个文件允许的线程访问数.打个通俗的比喻,Sema ...
- 职责链模式实例(C#)
下面是使用指责链模式的一个具体的实例,可以方便的理解指责链模式. 其实指责链简单的来说,就是把一个指责的流程定义出来,像一条链路一样,每一个结点有自己的处理范围,若不能处理,则传到自己的引用的下一个结 ...