You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 5.9中如何并发排序数组的非重叠子范围?

解决Swift并发排序数组非重叠子范围的问题

错误原因分析

编译器报错是因为Swift的并发安全规则禁止在并发任务中直接捕获并修改值类型变量(这里的someArray是Array值类型)。更关键的是,数组的**写时复制(CoW)**特性会导致每个任务执行sort()时,都会复制整个数组的内容——这不仅完全抵消了并发的性能收益,还会造成巨大的内存开销。

可行解决方案

方案一:使用UnsafeMutableBufferPointer直接操作内存

通过将数组绑定到UnsafeMutableBufferPointer,可以直接操作底层连续内存,分割出非重叠的子缓冲区供并发任务排序,既绕开CoW,又保证并发安全(因为子缓冲区范围完全不重叠,无数据竞争)。

修改后的代码:

let arraySize = 1_000_000
var someArray = [Int]()
someArray.reserveCapacity(arraySize)
for _ in 0..<arraySize {
    someArray.append(.random(in: 0...1_000_000))
}

let divisions = 6
var divisionLength = arraySize / divisions
if arraySize % divisions != 0 { divisionLength += 1 }

// 将数组绑定到可变缓冲区指针
someArray.withUnsafeMutableBufferPointer { buffer in
    Task {
        await withTaskGroup(of: Void.self) { taskGroup in
            var currentIndex = 0
            
            while currentIndex < buffer.count {
                let sliceStart = currentIndex
                let sliceEnd = min(currentIndex + divisionLength, buffer.count)
                currentIndex = sliceEnd
                
                // 捕获子缓冲区的范围,复制索引值避免闭包捕获可变变量
                taskGroup.addTask {
                    let subBuffer = buffer[sliceStart..<sliceEnd]
                    subBuffer.sort()
                }
            }
            
            await taskGroup.waitForAll()
        }
    }
}

方案二:手动拆分数组为独立子数组并发排序后合并

如果不想使用指针操作,可以先将原数组拆分为多个独立的子数组,并发排序后再合并回原数组。这种方式逻辑更直观,仅会产生子数组的少量内存开销:

let arraySize = 1_000_000
var someArray = [Int]()
someArray.reserveCapacity(arraySize)
for _ in 0..<arraySize {
    someArray.append(.random(in: 0...1_000_000))
}

let divisions = 6
var divisionLength = arraySize / divisions
if arraySize % divisions != 0 { divisionLength += 1 }

// 拆分原数组为子数组切片
var subArrays: [ArraySlice<Int>] = []
var currentIndex = 0
while currentIndex < someArray.endIndex {
    let end = min(currentIndex + divisionLength, someArray.endIndex)
    subArrays.append(someArray[currentIndex..<end])
    currentIndex = end
}

// 并发排序每个子数组并合并结果
let sortedSubArrays = await withTaskGroup(of: [Int].self) { taskGroup in
    for subArray in subArrays {
        taskGroup.addTask {
            subArray.sorted()
        }
    }
    
    var results = [Int]()
    results.reserveCapacity(arraySize)
    for await sortedSub in taskGroup {
        results.append(contentsOf: sortedSub)
    }
    return results
}

// 替换原数组
someArray = sortedSubArrays

关键注意事项

  • 无论哪种方案,必须保证子范围完全非重叠,否则会出现数据竞争或排序结果错误。
  • 使用指针操作时,withUnsafeMutableBufferPointer会自动管理指针生命周期,避免野指针问题,无需手动释放。
  • 方案二的合并步骤是串行的,但排序阶段是并发的,对于大规模数组依然能获得明显的性能提升。

内容的提问来源于stack exchange,提问作者JMA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 18:10:22