【问题标题】:Parallel array elements assignment causes crash in Swift并行数组元素分配导致 Swift 崩溃
【发布时间】:2021-04-22 01:57:36
【问题描述】:

我想同时设置数组的元素,所以我写代码来测试:

let Count = 1000
let SubRangeCount = 100
var ary = [Int](repeating: 0, count: Count)

let times = Count/SubRangeCount
let _ = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.concurrentPerform(iterations: times){ idx in
        for i in (idx * SubRangeCount)..<((idx+1) * SubRangeCount) {
            ary[i] = Int.random(in: 0...Int.max)
        }
    }
}

以上代码运行良好! 但是当我这样改变时:

let Count = 10000
let SubRangeCount = 1000

这是崩溃! 在操场上它抱怨“致命错误:UnsafeMutablePointer.initialize 重叠范围:文件 Swift/UnsafePointer.swift,第 832 行”

但是您可以看到数组的每个分配范围都没有重叠!

那么如何解决呢?谢谢!

【问题讨论】:

  • 即使是 1000 和 100,代码有时也会中断。
  • 是的!!!有时也会崩溃!
  • Swift 不提供线程安全:forums.swift.org/t/…
  • 实际上我很惊讶没有明确的“独占访问”错误。
  • "我想同时设置数组的元素" 你不能。这是对结构及其成员的同时访问,是被禁止的。

标签: arrays swift concurrency crash


【解决方案1】:

Array 在 Swift 上不是线程安全的,因此您需要同步对它的访问,例如使用 objc_sync_enterobjc_sync_exit

func synchronized<T : AnyObject, U>(_ obj: T, closure: () -> U) -> U {
    objc_sync_enter(obj)
    defer {
        objc_sync_exit(obj)
    }
    return closure()
}

var arr = [Int](repeating: 0, count: 100_000)
let obj = arr as NSObject // Object to synchronise

DispatchQueue.concurrentPerform(iterations: 100_000){ index in
    synchronized(obj) {
        arr[index] = Int.random(in: 0...Int.max)
    }
}

【讨论】:

    【解决方案2】:

    这里的数组是从多个线程并发变异的,这会产生竞争条件。为避免这种情况,您必须以线程安全的方式执行关键操作。您可以从 Apple 平台中的 Grand Central Dispatch 获得帮助 - 它让我们可以使用其更简单的基于队列的抽象来处理线程。

    let Count = 10000
    let SubRangeCount = 1000
    var ary = [Int](repeating: 0, count: Count)
    
    let times = Count/SubRangeCount
    let serialQueue = DispatchQueue(label: "serial.Queue")
    DispatchQueue.concurrentPerform(iterations: times){ idx in
        print(idx)
        for i in (idx * SubRangeCount)..<((idx+1) * SubRangeCount) {
            serialQueue.async {
                ary[i] = Int.random(in: 0...Int.max)
            }
        }
    }
    

    你也可以使用锁。

    let Count = 10000
    let SubRangeCount = 1000
    var ary = [Int](repeating: 0, count: Count)
    let lock = NSLock()
    
    let times = Count/SubRangeCount
    DispatchQueue.concurrentPerform(iterations: times){ idx in
        print(idx)
        for i in (idx * SubRangeCount)..<((idx+1) * SubRangeCount) {
            lock.lock()
            ary[i] = Int.random(in: 0...Int.max)
            lock.unlock()
        }
    }
    

    【讨论】:

      【解决方案3】:

      我找到了解决方法:

      DispatchQueue.concurrentPerform(iterations: enumTimes){ idx in
          
          var subAry = ary1[(idx * SubRangeCount)..<((idx+1) * SubRangeCount)]
          
          for i in subAry.indices {
              subAry[i] = Int.random(in: 0...Int.max)
          }
      }
      

      没有崩溃,比序列化作业快 3 到 10 倍。

      【讨论】:

      • 您不能同时更改Array,因为它不是线程安全的!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-08
      • 1970-01-01
      相关资源
      最近更新 更多