我想将给定的块添加到数组中,然后根据请求运行数组中包含的所有块。我有类似的代码:
class MyArrayBlockClass {
private var blocksArray: Array<() -> Void> = Array()
private let blocksQueue: NSOperationQueue()
func addBlockToArray(block: () -> Void) {
self.blocksArray.append(block)
}
func runBlocksInArray() {
for block in self.blocksArray {
let operation = NSBlockOperation(block: block)
self.blocksQueue.addOperation(operation)
}
self.blocksQueue.removeAll(keepCapacity: false)
}
}
问题在于 addBlockToArray 可以跨多个线程调用。发生的情况是在不同线程中快速连续调用 addBlockToArray,并且仅附加其中一项,因此在 runBlocksInArray 期间不会调用另一项。
我尝试过类似的方法,但似乎不起作用:
private let blocksDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
func addBlockToArray(block: () -> Void) {
dispatch_async(blocksDispatchQueue) {
self.blocksArray.append(block)
}
}
您已将
blocksDispatchQueue
定义为全局队列。将其更新为 Swift 3,相当于:
private let queue = DispatchQueue.global()
func addBlockToArray(block: @escaping () -> Void) {
queue.async {
self.blocksArray.append(block)
}
}
问题在于全局队列是并发队列,因此您无法实现所需的同步。但是如果您创建了自己的串行队列,那就没问题了,例如在 Swift 3 中:
private let queue = DispatchQueue(label: "com.domain.app.blocks")
默认情况下,此自定义队列是串行队列。这样您就可以实现您想要的同步。
注意,如果您使用此
blocksDispatchQueue
来同步与此队列的交互,则与此 blocksArray
的 all交互应通过此队列进行协调,例如还调度代码以使用同一队列添加操作:
func runBlocksInArray() {
queue.async {
for block in self.blocksArray {
let operation = BlockOperation(block: block)
self.blocksQueue.addOperation(operation)
}
self.blocksArray.removeAll()
}
}
或者,您也可以采用读取器/写入器模式,创建您自己的并发队列:
private let queue = DispatchQueue(label: "com.domain.app.blocks", attributes: .concurrent)
但是在读写器模式中,应该使用屏障来执行写入(实现类似串行的写入行为):
func addBlockToArray(block: @escaping () -> Void) {
queue.async(flags: .barrier) {
self.blocksArray.append(block)
}
}
但是您现在可以读取数据,如上所示:
let foo = queue.sync {
blocksArray[index]
}
此模式的好处是写入是同步的,但读取可以彼此同时发生。在这种情况下,这可能并不重要(因此一个简单的串行队列可能就足够了),但为了完整性,我包含了这个读写器模式。
另一种方法是
NSLock
的withLock
,它会为你平衡lock
和unlock
:
let lock = NSLock()
func addBlockToArray(block: @escaping () -> Void) {
lock.withLock {
blocksArray.append(block)
}
}
但是您现在可以读取数据,如上所示:
let foo = lock.withCriticalSection {
blocksArray[index]
}
历史上
NSLock
被认为性能较差而被忽视,但现在它甚至比 GCD 更快。
如果您正在寻找 Swift 2 示例,请参阅此答案的之前的版本。
对于线程之间的同步,请使用
dispatch_sync
(不是 _async)和您自己的调度队列(不是全局队列):
class MyArrayBlockClass {
private var queue = dispatch_queue_create("andrew.myblockarrayclass", nil)
func addBlockToArray(block: () -> Void) {
dispatch_sync(queue) {
self.blocksArray.append(block)
}
}
//....
}
dispatch_sync
很好而且易于使用,应该足以满足您的情况(我目前使用它来满足所有线程同步需求),但您也可以使用较低级别的锁和互斥锁。 Mike Ash 有一篇很棒的文章,介绍了不同的选择:锁、线程安全和 Swift
创建一个串行队列并在该线程中更改数组。 你的线程创建调用应该是这样的
private let blocksDispatchQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_SERIAL)
然后你就可以像现在一样使用它了。
func addBlockToArray(block: () -> Void) {
dispatch_async(blocksDispatchQueue) {
self.blocksArray.append(block)
}
}
import Foundation
class AtomicArray<T> {
private lazy var semaphore = DispatchSemaphore(value: 1)
private var array: [T]
init (array: [T]) { self.array = array }
func append(newElement: T) {
wait(); defer { signal() }
array.append(newElement)
}
subscript(index: Int) -> T {
get {
wait(); defer { signal() }
return array[index]
}
set(newValue) {
wait(); defer { signal() }
array[index] = newValue
}
}
var count: Int {
wait(); defer { signal() }
return array.count
}
private func wait() { semaphore.wait() }
private func signal() { semaphore.signal() }
func set(closure: (_ curentArray: [T])->([T]) ) {
wait(); defer { signal() }
array = closure(array)
}
func get(closure: (_ curentArray: [T])->()) {
wait(); defer { signal() }
closure(array)
}
func get() -> [T] {
wait(); defer { signal() }
return array
}
}
extension AtomicArray: CustomStringConvertible {
var description: String { return "\(get())"}
}
基本思想是使用常规数组的语法
let atomicArray = AtomicArray(array: [3,2,1])
print(atomicArray)
atomicArray.append(newElement: 1)
let arr = atomicArray.get()
print(arr)
atomicArray[2] = 0
atomicArray.get { currentArray in
print(currentArray)
}
atomicArray.set { currentArray -> [Int] in
return currentArray.map{ item -> Int in
return item*item
}
}
print(atomicArray)
import UIKit
class ViewController: UIViewController {
var atomicArray = AtomicArray(array: [Int](repeating: 0, count: 100))
let dispatchGroup = DispatchGroup()
override func viewDidLoad() {
super.viewDidLoad()
arrayInfo()
sample { index, dispatch in
self.atomicArray[index] += 1
}
dispatchGroup.notify(queue: .main) {
self.arrayInfo()
self.atomicArray.set { currentArray -> ([Int]) in
return currentArray.map{ (item) -> Int in
return item + 100
}
}
self.arrayInfo()
}
}
private func arrayInfo() {
print("Count: \(self.atomicArray.count)\nData: \(self.atomicArray)")
}
func sample(closure: @escaping (Int,DispatchQueue)->()) {
print("----------------------------------------------\n")
async(dispatch: .main, closure: closure)
async(dispatch: .global(qos: .userInitiated), closure: closure)
async(dispatch: .global(qos: .utility), closure: closure)
async(dispatch: .global(qos: .default), closure: closure)
async(dispatch: .global(qos: .userInteractive), closure: closure)
}
private func async(dispatch: DispatchQueue, closure: @escaping (Int,DispatchQueue)->()) {
for index in 0..<atomicArray.count {
dispatchGroup.enter()
dispatch.async {
closure(index,dispatch)
self.dispatchGroup.leave()
}
}
}
}
NSOperationQueue
本身是线程安全的,因此您可以将 suspended
设置为 true,从任何线程添加所需的所有块,然后将 suspended
设置为 false 以运行所有块。