Swift异步Task循环操作ThreadSafeCollection出现异常重复问题求助
问题原因分析
你的代码核心问题在于每个迭代任务最后返回的是整个集合的完整快照,而非自己添加的那500个元素,具体拆解如下:
firstIterate和secondIterate内部各自创建了一个并发Task,这两个Task会同时向safeCollection里添加0到499的数字,各执行500次添加操作。- 当第一个Task(比如
firstIterate里的)完成自身500次循环后,调用await safeCollection.getAll()时,第二个Task可能还没完成全部500次添加(比如刚加到494)。此时getAll()返回的数组里,包含了第一个Task添加的全部0-499,加上第二个Task已经添加的0-494。 - 等第二个Task完成自身500次循环后,再调用
getAll(),此时集合里已经有两个Task添加的全部1000个元素(0-499各两次)。 - 最后合并这两个快照数组,就会出现:
- 0-494:第一个快照里各有2次(两个Task都已添加),第二个快照里各有2次,总计4次;
- 495-499:第一个快照里只有1次(仅第一个Task添加),第二个快照里各有2次,总计3次。
修正思路
如果想得到每个数字恰好出现2次的结果,有两种常见可行方案:
让每个Task返回自己添加的元素,而非整个集合的快照:
@Sendable func firstIterate() async -> [Int] { let elements = Array(0..<500) for i in elements { await safeCollection.add(i) } return elements } @Sendable func secondIterate() async -> [Int] { let elements = Array(0..<500) for i in elements { await safeCollection.add(i) } return elements }这样两个Task各自返回自身添加的500个元素,合并后就是0-499各出现两次的结果。
在Actor内部维护元素计数,替代直接append的方式:
修改ThreadSafeCollection用字典统计每个数字的出现次数,最后再展开为数组:actor ThreadSafeCollection<T: Hashable> { private var counts: [T: Int] = [:] func add(_ element: T) { counts[element, default: 0] += 1 } func getAll() -> [T] { var result: [T] = [] for (element, count) in counts { result.append(contentsOf: Array(repeating: element, count: count)) } return result.sorted() } }这种方式下,无论Task的并发执行顺序如何,最终每个数字都会被统计到2次。
内容的提问来源于stack exchange,提问作者Sergey Mikhailov
相关产品推荐
相关产品推荐

