我正在使用具有QoS背景的串行队列
let serialQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.background)
分配一个作业同步,两个作业异步:
func serialTask() {
serialQueue.sync {
for i in 0..<10 {
print("", i)
}
}
serialQueue.async {
for i in 20..<30 {
print("⚪️", i)
}
}
serialQueue.async {
for i in 101..<120 {
print("", i)
}
}
}
所有3个作业一个接一个地执行同步,但最后两个作业是异步的。异步作业是否在串行队列中同步。
答案 0 :(得分:8)
让我看看我是否可以减轻您对async
与sync
的关注。
我将在我的示例中使用一些更改:
我会使用乐器&#39; &#34;兴趣点&#34;显示任务何时运行而不是print
语句。这样我们就可以用图形方式看到行为。
在发送内容时,我会发布一个简单的路标,并且我会使用路标范围以图形方式说明某个过程的持续时间。
我将for
循环更改为Thread.sleep(forTimeInterval: 1)
,模拟一些耗时的过程。如果你只是快速for
循环,事情就会发生得如此之快,以至于无法辨别线程的真实情况。
所以,请考虑:
enum SignPostCode: UInt32 { // some custom constants that I'll reference in Instruments
case seriesOfTasks = 0
case sync = 1
case async = 2
case dispatch = 3
}
enum SignPostColor: UInt { // standard color scheme for signposts in Instruments
case blue = 0
case green = 1
case purple = 2
case orange = 3
case red = 4
}
override func viewDidLoad() {
super.viewDidLoad()
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".serial")
// Because `seriesOfTasks` includes a synchronous call to something slow and we should never
// block the main thread, we will therefore start this from a global queue. It's not relevant to
// to the broader point here. You'd see the same behavior if you do this from the main thread), but
// it's best practice to never block the main thread.
DispatchQueue.global().async {
self.seriesOfTasks(on: queue)
}
}
private func seriesOfTasks(on queue: DispatchQueue) {
// flag start of `seriesOfTasks` (in blue)
kdebug_signpost_start(SignPostCode.seriesOfTasks.rawValue, 0, 0, 0, SignPostColor.blue.rawValue)
// do sync 1
kdebug_signpost(SignPostCode.dispatch.rawValue, 1, 0, 0, 0)
queue.sync {
self.oneSecondProcess(on: queue, with: .sync, arg1: 1, color: .green)
}
// do async 2
kdebug_signpost(SignPostCode.dispatch.rawValue, 2, 0, 0, 0)
queue.async {
self.oneSecondProcess(on: queue, with: .async, arg1: 2, color: .purple)
}
// do async 3
kdebug_signpost(SignPostCode.dispatch.rawValue, 3, 0, 0, 0)
queue.async {
self.oneSecondProcess(on: queue, with: .async, arg1: 3, color: .purple)
}
// flag end of `seriesOfTasks` (in blue)
kdebug_signpost_end(SignPostCode.seriesOfTasks.rawValue, 0, 0, 0, SignPostColor.blue.rawValue)
}
private func oneSecondProcess(on queue: DispatchQueue, with code: SignPostCode, arg1: UInt, color: SignPostColor) {
kdebug_signpost_start(code.rawValue, arg1, 0, 0, color.rawValue)
Thread.sleep(forTimeInterval: 1)
kdebug_signpost_end(code.rawValue, arg1, 0, 0, color.rawValue)
}
这就像你的例子,而不是print
语句,我们有kdebug
语句,在“工具”中产生以下图形时间线。 &#34;兴趣点&#34;工具:
所以,你可以看到:
seriesOfTasks
函数以蓝色显示sync
分派,第一个红色Ⓢ路标。
它等待绿色的sync
任务在继续之前完成,此时它会发出两个后续的调度,第二个和第三个红色Ⓢ路标(连续发生这么快它们在图中重叠。
但是seriesOfTasks
并没有等待紫色的两个async
任务完成。一旦完成调度这些async
任务,它就会立即返回。
因为后台队列是串行的,所以三个调度的任务(一个sync
,绿色,两个async
,紫色)相对于彼此串行运行。< / p>
如果您将其更改为使用并发队列,则:
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".concurrent", attributes: .concurrent)
DispatchQueue.global().async {
self.seriesOfTasks(on: queue)
}
然后你可以看到两个async
任务现在同时相互运行:
这一次,seriesOfTasks
发送sync
来电(等待它完成),然后,只有当sync
电话完成后,才能seriesOfTasks
继续发送两个async
调用(在这种情况下,不等待那些调度任务完成)。
如您所见,async
和sync
行为不同。使用sync
,调用线程将等待已调度的任务完成,但使用async
,它将赢得
答案 1 :(得分:2)
同步和异步执行与底层队列无关。同步执行意味着调用线程必须等到块完成。因此,在第一个块完成后,第二个块排队。 Asynchrounous意味着调用者不能等待块的完成。因此,第三个块在前一个serialQueue.async
语句之后直接入队,而第二个块仍在运行或甚至等待执行。
在函数serialTask()
结束时,保证执行第一个块。第二个和第三个块已排队,但不确定它们是否已执行,正在运行甚至等待执行。由于您正在使用串行队列,因此可以确保第二个块在第三个块之前执行。
您可以通过添加
来检查最后两个块的异步执行serialQueue.async {
print "sleep"
sleep(10);
print "awake"
}
在async
的两次调用之前,您将看到以下内容:
sleep
将立即打印。serialTask()
的执行时间大约不到10秒。awake
仅在10秒后输出(惊讶,惊讶)。serialTask()
结束很久之后,第二个和第三个块也会在10秒后执行。这两个块的迟执行意味着异步。如果您将第一个(同步)块移动到serialTask()
的末尾,则会发生以下情况:
sleep
将立即打印。serialTask()
的执行大约需要10秒钟。在执行结束时的同步块之后完成。awake
仅在10秒后输出(惊讶,惊讶)。awake
后执行。答案 2 :(得分:0)
所以同步任务立即运行,然后当它完成时,你将两个异步任务添加到该队列,*然后你的方法返回。 *这两个任务将按顺序处理,一旦该队列空闲。
因此,如果在调用此方法之前您在该队列中有任何挂起的异步任务,那么这些任务将在您的两个异步任务之前运行。