Swift - GCD 可变数组多线程发出“在枚举时发生突变”
Posted
技术标签:
【中文标题】Swift - GCD 可变数组多线程发出“在枚举时发生突变”【英文标题】:Swift - GCD mutable array multiple threads issue "mutated while being enumerated" 【发布时间】:2015-04-28 01:11:21 【问题描述】:我目前正在开发一款球体从天而降的游戏。在收集球体时,您会获得积分,并且在获得一定数量的积分后,所有球体都会加速到另一个速度。
新球体不断添加到阵列中(每个 SKNode 内有 4 个球体)。 当它们要加速时,我会遍历数组以提高所有它们的速度。 当球体从屏幕上掉下来时,我将它们从阵列中移除。
class GameScene: SKScene, SKPhysicsContactDelegate
...
var allActiveNodes = Array<SKNode>()
private let concurrentNodesQueue = dispatch_queue_create(
"com.SphereHunt.allActiveNodesQueue", DISPATCH_QUEUE_CONCURRENT)
...
//1. This is where the new spheres are added to the Array via a new thread
func addSpheres(leftSphere: Sphere, middleLeftSphere: Sphere, middleRightSphere: Sphere, rightSphere: Sphere)
...
dispatch_barrier_async(self.concurrentNodesQueue)
self.allActiveNodes.append(containerNode)
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue)
//Set the new spheres in motion
self.runPastAvatar(containerNode)
//2. This function starts a thread that will increase the speed of all active spheres
func increaseSpeed20percent()
durationPercentage = durationPercentage * 0.8
dispatch_sync(self.concurrentNodesQueue)
let copyAllActiveNodes = self.allActiveNodes
let count = copyAllActiveNodes.count
for index in 0...count-1
let node = copyAllActiveNodes[index]
node.removeAllActions()
self.runPastAvatar(node)
//3. This method removes the sphere that is not in screen anymore from the Array
func removeLastNode(node: SKNode)
dispatch_barrier_async(self.concurrentNodesQueue)
self.allActiveNodes.removeAtIndex(0)
node.removeFromParent()
println("Removed")
我不确定我是否正确理解了 GCD,我尝试了多种解决方案,这是我确信会起作用的解决方案。我总是以同样的错误信息告终:
*** Terminating app due to uncaught exception 'NSGenericException',
reason: '*** Collection <__NSArrayM: 0x17004c9f0> was mutated while being enumerated.'
如何让线程在处理数组时不相互干扰?
【问题讨论】:
【参考方案1】:我不确定这是否是问题,但来自以下文件:
func dispatch_sync(_ queue: dispatch_queue_t,
_ block: dispatch_block_t)
与 dispatch_async 不同,不会在目标上执行保留 队列。因为对该函数的调用是同步的,所以它“借用”了 调用者的参考。此外,没有对 Block_copy 执行 块。
> 作为优化,此函数调用当前块 尽可能线程。
我在这里加粗了重要的部分。为什么不用dispatch_barrier_sync
来调用循环。
【讨论】:
我尝试使用 dispatch_barrier_sync 调用循环,但问题仍然存在。 我注释掉了与数组有关的所有内容,但仍然收到相同的错误消息。这很奇怪,因为没有其他数组是这样操作的。我该如何调试呢?错误消息“Collection <__nsarraym:> ...”显示了数组的 ID,但是在查看代码时,数组没有得到任何 ID。我怎样才能找到背后处理的特定数组,从而给出此错误消息? 我把所有的东西都注释掉了。我现在只启动 1 个添加更多球体的线程,然后休眠一段时间。当线程休眠时,我总是会收到相同的错误消息,除了休眠线程之外没有其他任何东西在运行。线程内的睡眠功能会不会有任何问题?但是这样的事情不会产生“<__nsarraym:> 在被枚举时发生了变异。”【参考方案2】:我的问题是我使用线程睡眠解决方案在时间间隔内发射新球体。这是一个糟糕的选择,但我认为不应该产生这样的错误消息。我使用 NSTimer 在一个时间间隔内发射新球体来解决它。这给游戏带来了一点延迟,但它更强大并且不会崩溃。接下来是找出如何使用 NSTimer 而不会在游戏中造成这样的延迟!
【讨论】:
以上是关于Swift - GCD 可变数组多线程发出“在枚举时发生突变”的主要内容,如果未能解决你的问题,请参考以下文章
Swift - 多线程实现方式 - Grand Central Dispatch(GCD)