concurrentPerform UnsafeMutablePointer.deinitialize 向数组添加值时出错

Posted

技术标签:

【中文标题】concurrentPerform UnsafeMutablePointer.deinitialize 向数组添加值时出错【英文标题】:concurrentPerform UnsafeMutablePointer.deinitialize error while adding value to array 【发布时间】:2017-12-24 09:12:29 【问题描述】:

向数组添加值时出现并发执行错误。我按下按钮。 在错误的那一刻 myArray01 有 133 。在其他运行 myArray01 有 69 个元素。如何消除此错误?

在错误行

线程 8:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)

在控制台

致命错误:UnsafeMutablePointer.deinitialize 为负数 2017-12-24 11:59:53.438933+0300 ap02[7624:1873749] 致命错误: UnsafeMutablePointer.deinitialize 负数 (lldb)

类似的话题 Swift:UnsafeMutablePointer.deinitialize 附加到数组时带有负数的致命错误 Swift: UnsafeMutablePointer.deinitialize fatal error with negative count when appending to array

var myArray01 = [4444,5555]

@IBAction func button01Pressed(_ sender: Any) 
    self.doIt01()


func doIt01() 
    DispatchQueue.concurrentPerform(iterations: 1000)  iter in
        var max = 100000
        for iterB in 0..<100000 
            var k = 0
            k = k + 1
            var half:Int = max/2
            if (iterB == half) 
                myArray01.append(iter)
            
        
    

【问题讨论】:

感谢 Riajur Ra​​hman。从这里制作 SynchronizedArray 的解决方案(在 swift 中创建线程安全数组)适用于 rmooney ***.com/questions/28191079/… 【参考方案1】:

根本问题是您同时从多个线程将项目附加到数组中。 Swift Array 类型不是线程安全的。你必须synchronize你与它的互动。例如,您可以使用串行队列自行同步:

DispatchQueue.global().async 
    let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".synchronization")

    var array = [4444,5555]

    DispatchQueue.concurrentPerform(iterations: 1000)  iter in
        var max = 100_000
        for iterB in 0 ..< 100_000 
            var k = 0
            k = k + 1
            var half = max/2
            if iterB == half 
                queue.async 
                    array.append(iter)
                
            
        
    

    queue.async 
        print(array)
    

注意,我还建议将concurrentPerform 分派到后台线程,因为虽然它并行运行代码,但它确实会阻塞当前线程,直到所有这些并行循环都完成。


或者您可以使用为您进行同步的数组类型:

DispatchQueue.global().async 
    let array = SynchronizedArray(array: [4444,5555])

    DispatchQueue.concurrentPerform(iterations: 1000)  iter in
        let max = 100_000
        for iterB in 0 ..< 100_000 
            var k = 0
            k = k + 1
            let half = max/2
            if iterB == half 
                array.append(iter)
            
        
    

    print(array)

在哪里

//
//  SynchronizedArray.swift
//
//  Created by Robert Ryan on 12/24/17.
//

import Foundation

/// A synchronized, ordered, random-access array.
///
/// This provides low-level synchronization for an array, employing the
/// reader-writer pattern (using concurrent queue, allowing concurrent
/// "reads" but using barrier to ensure nothing happens concurrently with
/// respect to "writes".
///
/// - Note: This may not be sufficient to achieve thread-safety,
///         as that is often only achieved with higher-level synchronization.
///         But for many situations, this can be sufficient.

final class SynchronizedArray<Element> 
    private var array: [Element]
    private let queue: DispatchQueue

    init(array: [Element] = [], qos: DispatchQoS = .default) 
        self.array = array
        self.queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".synchronization", qos: qos, attributes: .concurrent)
    

    /// First element in the collection.

    var first: Element? 
        return queue.sync  self.array.first 
    

    /// Last element in the collection.

    var last: Element? 
        return queue.sync  self.array.last 
    

    /// The number of elements in the collection.

    var count: Int 
        return queue.sync  self.array.count 
    

    /// Inserts new element at the specified position.
    ///
    /// - Parameters:
    ///   - newElement: The element to be inserted.
    ///   - index: The position at which the element should be inserted.

    func insert(_ newElement: Element, at index: Int) 
        queue.async(flags: .barrier) 
            self.array.insert(newElement, at: index)
        
    

    /// Appends new element at the end of the collection.
    ///
    /// - Parameter newElement: The element to be appended.

    func append(_ newElement: Element) 
        queue.async(flags: .barrier) 
            self.array.append(newElement)
        
    

    /// Removes all elements from the array.

    func removeAll() 
        queue.async(flags: .barrier) 
            self.array.removeAll()
        
    

    /// Remove specific element from the array.
    ///
    /// - Parameter index: The position of the element to be removed.

    func remove(at index: Int) 
        queue.async(flags: .barrier) 
            self.array.remove(at: index)
        
    

    /// Retrieve or update the element at a particular position in the array.
    ///
    /// - Parameter index: The position of the element.

    subscript(index: Int) -> Element 
        get 
            return queue.sync  self.array[index] 
        
        set 
            queue.async(flags: .barrier)  self.array[index] = newValue 
        
    

    /// Perform a writer block of code asynchronously within the synchronization system for this array.
    ///
    /// This is used for performing updates, where no result is returned. This is the "writer" in
    /// the "reader-writer" pattern, which performs the block asynchronously with a barrier.
    ///
    /// For example, the following checks to see if the array has one or more values, and if so,
    /// remove the first item:
    ///
    ///     synchronizedArray.writer  array in
    ///         if array.count > 0 
    ///             array.remove(at: 0)
    ///         
    ///     
    ///
    /// In this example, we use the `writer` method to avoid race conditions between checking
    /// the number of items in the array and the removing of the first item.
    ///
    /// If you are not performing updates to the array itself or its values, it is more efficient
    /// to use the `reader` method.
    ///
    /// - Parameter block: The block to be performed. This is allowed to mutate the array. This is
    ///                    run on a private synchronization queue using a background thread.

    func writer(with block: @escaping (inout [Element]) -> Void) 
        queue.async(flags: .barrier) 
            block(&self.array)
        
    

    /// Perform a "reader" block of code within the synchronization system for this array.
    ///
    /// This is the "reader" in the "reader-writer" pattern. This performs the read synchronously,
    /// potentially concurrently with other "readers", but never concurrently with any "writers".
    ///
    /// This is used for reading and performing calculations on the array, where a whole block of
    /// code needs to be synchronized. The block may, optionally, return a value.
    /// This should not be used for performing updates on the array itself. To do updates,
    /// use `writer` method.
    ///
    /// For example, if dealing with array of integers, you could average them with:
    ///
    ///     let average = synchronizedArray.reader  array -> Double in
    ///         let count = array.count
    ///         let sum = array.reduce(0, +)
    ///         return Double(sum) / Double(count)
    ///     
    ///
    /// This example ensures that there is no race condition between the checking of the
    /// number of items in the array and the calculation of the sum of the values.
    ///
    /// - Parameter block: The block to be performed. This is not allowed to mutate the array.
    ///                    This runs on a private synchronization queue (so if you need main queue,
    ///                    you will have to dispatch that yourself).

    func reader<U>(block: ([Element]) -> U) -> U 
        return queue.sync 
            block(self.array)
        
    

    /// Retrieve the array for use elsewhere.
    ///
    /// This resulting array is a copy of the underlying `Array` used by `SynchronizedArray`.
    /// This copy will not participate in any further synchronization.

    var copy: [Element]  return queue.sync  self.array  


extension SynchronizedArray: CustomStringConvertible 
    // Use the description of the underlying array

    var description: String  return queue.sync  self.array.description  

【讨论】:

嗨 Rob,为什么我们需要使用 queue.sync 而不是 queue.async 进行读取?由于写入时带有.barrier 标志,数据不会更改,因此两者都无法工作? 这是“reader-writer”模式,其中一个确实使用屏障异步写入(调用者无需等待),但没有屏障同步读取(因为调用者可能需要该值才能继续; 这就是为什么它毕竟在阅读)。从理论上讲,您可以异步执行所有操作,但这会使调用点变得更加复杂而没有什么好处。如果不使用完成处理程序闭包或类似的东西,你就不能异步“读取”。对于一个简单的同步机制(通常非常快),上面的模式是最简单的。 现在,如果您正在同步存储可能非常慢的内容(例如,从持久存储或网络读取),那么您肯定会异步执行读取和写入操作,并且会增加复杂性。但是对于简单的读写器模式,在处理快速结构时,同步读取是最简单的。 哦,我明白了。所以原因是 API 更简单,因为我们不需要完成处理程序,而是可以直接读取值。这就说得通了。感谢您的澄清!

以上是关于concurrentPerform UnsafeMutablePointer.deinitialize 向数组添加值时出错的主要内容,如果未能解决你的问题,请参考以下文章

concurrentPerform UnsafeMutablePointer.deinitialize 向数组添加值时出错

GCD 使用若干注意事项

UnsafeMutableRawPointer 到 UnsafeMutablePointer<T>:指针上的 EXC_BAD_ACCESS