在说这两个类型之前,先介绍一个函数:
func dispatchPrecondition(condition: @autoclosure () -> DispatchPredicate)
这个函数可以用来确保当前多线程操作的可靠性,比如:
aDispatchQueue.async {
dispatchPrecondition(condition: .onQueue(.main))
self.view.alpha = 1
}
以上代码可以确保在aDispatchQueue中操作UI属性前,确保此线程是主线程。
如果当前线程不是主线程,后续操作不会发生。其效果与以下代码的效果相似:
guard Thread.isMainThread else { return }
DispatchPredicate
public enum DispatchPredicate {
case onQueue(DispatchQueue)
case onQueueAsBarrier(DispatchQueue)
case notOnQueue(DispatchQueue)
}
onQueue, notOnQueue是很容易理解的,确保当前队列是不是某队列。
关于onQueueAsBarrier,请看这个示例:
let concurrentQueue = DispatchQueue(label: "queue", attributes: .concurrent)
var numbers = [0, 1]
concurrentQueue.async { print(numbers.first!) }
concurrentQueue.async(flags: .barrier) {
dispatchPrecondition(condition: .onQueueAsBarrier(concurrentQueue))
Thread.sleep(forTimeInterval: 0.25)
numbers = [2]
}
concurrentQueue.async { print(numbers.last!) }
以上代码会输出:
0
2
async(flags: .barrier) 可以使操作具有独占性,在并发队列中,可以防止发生数据竞争。
在这里,dispatchPrecondition(condition: .onQueueAsBarrier(q)) 可以确保当前block中的操作都是在concurrentQueue中具有独占性的(barrier操作),也就是在修改numbers数组时,不会有其他线程同时在对numbers进行操作。
在处理读写并发操作时,如果你的读写逻辑代码比较分散,这个条件就可以很高效地确保分散在各处的写操作具有独占性,防止读写操作同时进行导致意外(返回结果不符合预期、程序崩溃等)发生!
DispatchWorkItem
public class DispatchWorkItem {
public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> Void)
public func perform()
public func wait()
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
public func notify(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, queue: DispatchQueue, execute: @escaping @convention(block) () -> Void)
public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
public func cancel()
public var isCancelled: Bool { get }
}
其核心就是封装一个可以执行的闭包。
可以通过perform()方法,直接执行内部的闭包。
也可以将DispatchWorkItem对象添加到DispatchQueue当中,然后由DispatchQueue去调度执行DispatchWorkItem内部的闭包。
let workItem = DispatchWorkItem.init { print("working") }
DispatchQueue.global().async { workItem.perform() }
DispatchQueue.global().async(execute: workItem)
DispatchWorkItemFlags
上面的示例代码中其实已经用到了DispatchWorkItemFlags.barrier
concurrentQueue.async(flags: .barrier)
如果你去查看关于DispatchWorkItemFlags的官网文档,你会发现官网并没有太详细的解释,尤其是对以下几个参数没有具体的介绍。
static let assignCurrentContext: DispatchWorkItemFlags
static let barrier: DispatchWorkItemFlags
static let detached: DispatchWorkItemFlags
static let enforceQoS: DispatchWorkItemFlags
static let inheritQoS: DispatchWorkItemFlags
static let noQoS: DispatchWorkItemFlags
-_-+ 其实,你应该查看的是关于dispatch_block_flags_t的介绍。
所以根据以上文档,可以这样理解以上几个参数:
- assignCurrentContext
表明DispatchWorkItem在被创建时,应该被指定执行上下文参数。这些参数包括:QoS class, os_activity_t 和进程间通信请求参数。
如果DispatchWorkItem被直接调用,DispatchWorkItem在调用的线程中将采用这些参数。
如果DispatchWorkItem被提交到队列中,这些参数会被提交时的执行上下文中的参数替代。
如果QoS类为DISPATCH_BLOCK_NO_QOS_CLASS或dispatch_block_create_with_qos_class生成的值,那么这个值会取代当前的值。
- barrier
这个是比较常用的参数。
如果DispatchWorkItem被提交到.concurrent并发队列,那么这个DispatchWorkItem中的操作会具有独占性(防止此DispatchWorkItem中的block内的操作与其他操作同时执行)。
如果直接执行这个DispatchWorkItem,没有任何效果。
以下代码仅供参考:
let q = DispatchQueue(label: "ficow-test-queue", attributes: .concurrent)
q.async {
print("a")
}
q.async(flags: .barrier) {
Thread.sleep(forTimeInterval: 0.25)
print("barrier")
}
q.async {
print("b")
}
以上代码会输出:
a
barrier
b
如果没有.barrier这个属性,输出内容将是:
a
b
barrier
- detached
表明DispatchWorkItem会无视当前执行上下文的参数(QoS class, os_activity_t 和进程间通信请求参数)。
如果直接执行DispatchWorkItem,在复制这些属性给这个block前,block在执行期间会移除在调用线程中的这些属性。
如果DispatchWorkItem被添加到队列中,block在执行时会采用队列的属性,或者赋值给block的属性。
- enforceQoS
表明DispatchWorkItem会采用当前的QoS class,而不是队列的。
- inheritQoS
表明DispatchWorkItem会采用队列的QoS class,而不是当前的。
- noQoS
不指定QoS,由调用线程或队列来指定。
参考文章:
DispatchWorkItemFlags
dispatch_block_flags_t
Swift多线程编程总结
网友评论