
成都创新互联公司专注于调兵山企业网站建设,
响应式网站开发,
商城网站开发。调兵山网站建设公司,为调兵山等地区提供建站服务。全流程按需设计网站,专业设计,全程项目跟踪,
成都创新互联公司专业和态度为您提供的服务
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 是线程不安全的。
另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
网页题目:SwiftArraycopy的线程安全问题-创新互联
链接分享:
http://kswsj.cn/article/jiohi.html