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

时间:2018-03-13 06:31:07

标签: swift multithreading grand-central-dispatch

我正在使用具有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个作业一个接一个地执行同步,但最后两个作业是异步的。异步作业是否在串行队列中同步。

3 个答案:

答案 0 :(得分:8)

让我看看我是否可以减轻您对asyncsync的关注。

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

  1. 我会使用乐器&#39; &#34;兴趣点&#34;显示任务何时运行而不是print语句。这样我们就可以用图形方式看到行为。

    在发送内容时,我会发布一个简单的路标,并且我会使用路标范围以图形方式说明某个过程的持续时间。

  2. 我将for循环更改为Thread.sleep(forTimeInterval: 1),模拟一些耗时的过程。如果你只是快速for循环,事情就会发生得如此之快,以至于无法辨别线程的真实情况。

  3. 所以,请考虑:

    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;工具:

    enter image description here

    所以,你可以看到:

    1. seriesOfTasks函数以蓝色显示sync分派,第一个红色Ⓢ路标。

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

    3. 但是seriesOfTasks并没有等待紫色的两个async任务完成。一旦完成调度这些async任务,它就会立即返回。

    4. 因为后台队列是串行的,所以三个调度的任务(一个sync,绿色,两个async,紫色)相对于彼此串行运行。< / p>

    5. 如果您将其更改为使用并发队列,则:

      let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".concurrent", attributes: .concurrent)
      
      DispatchQueue.global().async {
          self.seriesOfTasks(on: queue)
      }
      

      然后你可以看到两个async任务现在同时相互运行:

      enter image description here

      这一次,seriesOfTasks发送sync来电(等待它完成),然后,只有当sync电话完成后,才能seriesOfTasks继续发送两个async调用(在这种情况下,不等待那些调度任务完成)。

      如您所见,asyncsync行为不同。使用sync,调用线程将等待已调度的任务完成,但使用async,它将赢得

答案 1 :(得分:2)

同步和异步执行与底层队列无关。同步执行意味着调用线程必须等到块完成。因此,在第一个块完成后,第二个块排队。 Asynchrounous意味着调用者不能等待块的完成。因此,第三个块在前一个serialQueue.async语句之后直接入队,而第二个块仍在运行或甚至等待执行。

在函数serialTask()结束时,保证执行第一个块。第二个和第三个块已排队,但不确定它们是否已执行,正在运行甚至等待执行。由于您正在使用串行队列,因此可以确保第二个块在第三个块之前执行。

您可以通过添加

来检查最后两个块的异步执行
serialQueue.async {
    print "sleep"
    sleep(10);
    print "awake"
}

async的两次调用之前,您将看到以下内容:

  1. 第一个块将立即执行,sleep将立即打印。
  2. serialTask()的执行时间大约不到10秒。
  3. awake仅在10秒后输出(惊讶,惊讶)。
  4. serialTask()结束很久之后,第二个和第三个块也会在10秒后执行。
  5. 这两个块的执行意味着异步。如果您将第一个(同步)块移动到serialTask()的末尾,则会发生以下情况:

    1. sleep将立即打印。
    2. serialTask()的执行大约需要10秒钟。在执行结束时的同步块之后完成。
    3. awake仅在10秒后输出(惊讶,惊讶)。
    4. 第二个和第三个块也会在打印出awake后执行。

答案 2 :(得分:0)

所以同步任务立即运行,然后当它完成时,你将两个异步任务添加到该队列,*然后你的方法返回。 *这两个任务将按顺序处理,一旦该队列空闲

因此,如果在调用此方法之前您在该队列中有任何挂起的异步任务,那么这些任务将在您的两个异步任务之前运行。