2

I have concurrentPerform error while adding value to array. I press button. At the moment of error myArray01 have 133 . In other run myArray01 have 69 elements . How remove this error?

at line of error

Thread 8: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

at console

fatal error: UnsafeMutablePointer.deinitialize with negative count 2017-12-24 11:59:53.438933+0300 ap02[7624:1873749] fatal error: UnsafeMutablePointer.deinitialize with negative count (lldb)

similar topic Swift: UnsafeMutablePointer.deinitialize fatal error with negative count when appending to array 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 Rahman
  • 1,976
  • 19
  • 28
  • thanks Riajur Rahman . solution that make SynchronizedArray from here (Create thread safe array in swift ) works from rmooney https://stackoverflow.com/questions/28191079/create-thread-safe-array-in-swift – userForStackExchange Dec 24 '17 at 16:18

1 Answers1

9

The fundamental problem is that you are appending items to an array from multiple threads at the same time. The Swift Array type is not thread-safe. You have to synchronize your interaction with it. For example, you can synchronize it yourself using a serial queue:

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)
    }
}

Note, I'd also suggest dispatching the concurrentPerform to a background thread because while it runs code in parallel, it does block the current thread until all of those parallel loops are done.


Or you can use an array type that does the synchronization for you:

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)
}

Where

//
//  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
  • 415,655
  • 72
  • 787
  • 1,044
  • Hi Rob, why for reads do we need to use `queue.sync` rather than `queue.async`? Couldn't either work since the data will not change due to the `.barrier` flag on writes? – user3492121 Dec 15 '21 at 21:04
  • 1
    This is the “reader-writer” pattern, where one does writes asynchronously with barrier (no need for the caller to wait), but reads synchronously without barrier (because the caller presumably needs the value to proceed; that's why it’s reading after all). Theoretically, you could do everything asynchronously, but it makes the calling point more complicated with little benefit. You can’t “read” asynchronously without encumbering yourself with a completion handler closure or the like. For a simple synchronization mechanism (which is generally very fast), the above pattern is simplest. – Rob Dec 15 '21 at 22:38
  • 1
    Now if you were synchronizing something for which the store might be very slow (e.g., reading from persistent store or network), then you would definitely do both reads and writes asynchronously and live with the added complexity. But for simple reader-writer pattern when dealing with a fast structure, synchronous reads are easiest. – Rob Dec 15 '21 at 22:40
  • 1
    Oh I see. So the reason being that the API is simpler as we wouldn't have to have a completion handler and instead can just read the value directly. That makes sense. Thanks for the clarification! – user3492121 Dec 15 '21 at 22:41