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 的线程安全问题的更多相关文章

  1. 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

    一. 各类数据结构比较及其线程安全问题 1. Array(数组): 分配在连续内存中,不能随意扩展,数组中数值类型必须是一致的.数组的声明有两种形式:直接定义长度,然后赋值:直接赋值. 缺点:插入数据 ...

  2. 【C#复习总结】探究各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

    前言 先普及一下线程安全和类型安全 线程安全: 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的 ...

  3. 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的无状态模 ...

  4. .NET面试题系列(五)数据结构(Array、List、Queue、Stack)及线程安全问题

    常用数据结构的时间复杂度 如何选择数据结构 Array (T[]) 当元素的数量是固定的,并且需要使用下标时. Linked list (LinkedList<T>) 当元素需要能够在列表 ...

  5. iOS开发线程安全问题

    先来看一下代码: - (void)viewDidLoad { [super viewDidLoad]; self.testStr = @"String initial complete&qu ...

  6. C# 多线程之List的线程安全问题

    网上关于List的线程安全问题将的很少,所以自己实验了一把,发现确实是线程不安全的.所以当你在进行多线程编程中使用了共享的List集合,必须对其进行线程安全处理. List的Add方法是线程不安全的, ...

  7. 关于 SimpleDateFormat 的非线程安全问题及其解决方案

    一直以来都是直接用SimpleDateFormat开发的,没想着考虑线程安全的问题,特记录下来(摘抄的): 1.问题: 先来看一段可能引起错误的代码: package test.date; impor ...

  8. Core Data 的线程安全问题

    前言: 很多小的App只需要一个ManagedContext在主线程就可以了,但是有时候对于CoreData的操作要耗时很久的,比如App开启的时候要载入大量数据,如果都放在主线程,毫无疑问会阻塞UI ...

  9. Java 线程安全问题的本质

    原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94 目录: 线程安全问题的本质 理解CPU JVM虚拟机类比于操作系统 重排序 汇总 一些解释 ...

随机推荐

  1. c语言内存对齐(1)

    在<C陷阱与缺陷>有这样一个例子: #include <stdio.h> int main(void) { int i; char c; ;i<;i++){ scanf( ...

  2. 【MS SQL】通过执行计划来分析SQL性能

    原文:[MS SQL]通过执行计划来分析SQL性能 如何知道一句SQL语句的执行效率呢,只知道下面3种: 1.通过SQL语句执行时磁盘的活动量(IO)信息来分析:SET STATISTICS IO O ...

  3. mysql基础之存储引擎

    原文:mysql基础之存储引擎 数据库对同样的数据,有着不同的存储方式和管理方式,在mysql中,称为存储引擎 常用的表的引擎 Myisam ,批量插入速度快, 不支持事务,锁表 Innodb, 批量 ...

  4. 阿里云WinServer2008下配置IIS7支持php

    先送一只法克鱿给百度,百度了n多的方法都或多或少有问题. 0.php安装包 php-5.2.1-Win32.zip 下载地址 http://pan.baidu.com/s/1pJuc8YZ 最开始是p ...

  5. 简单的三层asp.net webForm使用Ninject实现Ioc

    简单的三层asp.net webForm使用Ninject实现Ioc 在asp.net webform下使用Ninject的简单过程. 首先建立个项目,如下图,简单三层(PS:UI层要同时引用BLL. ...

  6. 最小的MVC工程

    asp.net mvc笔记一,最小的MVC工程   Asp.net MVC项目默认会引用很多第三方插件,特别是现在的5.0,默认示例项目就几十M,搞得都不知道那些才是MVC必须的,是重点,那些是可有可 ...

  7. 第一章 CLR 的执行模型

    CLR via C# 读书笔记:第一章 CLR 的执行模型(1) 第Ⅰ部分CLR基础.这部分为三章(第一章:CLR的只想能够模型,第二章:生成.打包.部署和管理应用程序及类型,第三章:共享程序集和强命 ...

  8. 安装mysql-python报错:UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 65: ordinal not in range(128)

    安装mysql-python报错: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 65: ordinal n ...

  9. [转]Installing Snow Leopard (Client) on VMware Fusion 6.0.3

    Source: http://inficererk.wordpress.com/2014/05/29/installing-snow-leopard-client-on-vmware-fusion-6 ...

  10. 【C基础】const用法

    1.const 和 define 异同 同:const 和 define都是修饰常量 异:const修饰的常量只是编译器的一种优化,它是可以通过内存地址修改const修饰的常量:而define修饰的常 ...