在GCD中是通过异步操作Swift进行串行队列同步

sal*_*qui 5 multithreading grand-central-dispatch swift

我正在使用具有QoS背景的串行队列

let serialQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.background)
Run Code Online (Sandbox Code Playgroud)

分配一个作业同步,两个作业异步:

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所有3个作业一个接一个地执行同步,但最后两个作业是异步的.同步作业是否在串行队列中同步.

Rob*_*Rob 9

让我看看,如果我能减轻你的了担忧asyncsync.

我将在我的示例中使用的一些更改:

  1. 我将使用Instruments的"兴趣点"来显示任务何时运行而不是print语句.这样我们就可以用图形方式查看行为.

    我会在发送一些东西时发布一个简单的路标,我将使用一个路标范围以图形方式说明某个过程的持续时间.

  2. 我会将你的for循环改为a 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)
}
Run Code Online (Sandbox Code Playgroud)

这就像你的例子,而不是print声明,我们有kdebug声明,在乐器的"兴趣点"工具中产生以下图形时间线:

在此输入图像描述

所以,你可以看到:

  1. seriesOfTasks功能以蓝色发出sync调度,第一个红色Ⓢ路标.

  2. 它等待sync绿色的任务在继续之前完成,此时它会发出两个后续的调度,第二个和第三个红色Ⓢ路标(它们连续发生很快,它们在图中重叠).

  3. 但是seriesOfTasks不要等待async紫色的两个任务完成.一旦完成调度这些async任务,它立即返回.

  4. 因为后台队列是串行的,所以三个调度的任务(一个sync是绿色,两个async是紫色)相互之间串行运行.

但是,如果将其更改为使用并发队列,则:

let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".concurrent", attributes: .concurrent)

DispatchQueue.global().async {
    self.seriesOfTasks(on: queue)
}
Run Code Online (Sandbox Code Playgroud)

然后你可以看到这两个async任务现在彼此同时运行:

在此输入图像描述

这一次,seriesOfTasks调度sync呼叫(等待其完成),然后,只有当sync调用完成可seriesOfTasks继续派遣两个async呼叫(在这种情况下,不等待那些派遣任务完成).

如您所见,行为asyncsync行为是不同的.随着sync调用线程将等待分派任务来完成,但是async,它不会.