Grand Central Dispatches - 执行无序?

时间:2015-04-10 02:51:12

标签: swift grand-central-dispatch

我正在努力更好地理解GCD,所以我在下面编写了测试代码(Bottom)。基本上有两个函数,在不同的队列上发送等待,在主线程上的println等待特定的任务。

我希望控制台输出应该是:

BEFORE FUNCTIONS
start 3 sec loop
start 5 sec loop
end 3 sec loop
BETWEEN FUNCTIONS WAIT ON LONG
AFTER FUNCTIONS WAIT ON LONG
end 5 sec loop
BETWEEN FUNCTIONS WAIT ON LONGER
AFTER FUNCTIONS WAIT ON LONGER

但我得到的是:

BEFORE FUNCTIONS
start 3 sec loop
start 5 sec loop
BETWEEN FUNCTIONS WAIT ON LONGER
AFTER FUNCTIONS WAIT ON LONGER
end 3 sec loop
BETWEEN FUNCTIONS WAIT ON LONG
AFTER FUNCTIONS WAIT ON LONG
end 5 sec loop

一些事情对我来说没有意义 - 1.为什么在“长”之前打印“更长”的? 2.为什么在较长的5秒函数的 END 之后没有打印“更长”的那个?

CODE(ViewController.swift)

import UIKit

class ViewController: UIViewController {

  var longQueue = dispatch_group_create()
  var longerQueue = dispatch_group_create()
  var queueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
  var queueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    println("BEFORE FUNCTIONS")

    dispatch_async(queueHigh) {
      self.longFunc()
    }

    dispatch_group_notify(longQueue, dispatch_get_main_queue()) {
      println("BETWEEN FUNCTIONS WAIT ON LONG")
    }
    dispatch_group_notify(longerQueue, dispatch_get_main_queue()) {
      println("BETWEEN FUNCTIONS WAIT ON LONGER")
    }

    dispatch_async(queueLow) {
      self.longerFunc()
    }

    dispatch_group_notify(longQueue, dispatch_get_main_queue()) {
      println("AFTER FUNCTIONS WAIT ON LONG")
    }
    dispatch_group_notify(longerQueue, dispatch_get_main_queue()) {
      println("AFTER FUNCTIONS WAIT ON LONGER")
    }
  }

  func longFunc () {
    dispatch_group_enter(self.longQueue)
      println("start 3 sec loop")
      sleep(3)
      println("end 3 sec loop")
    dispatch_group_leave(self.longQueue)
  }

  func longerFunc() {
    dispatch_group_enter(self.longerQueue)
      println("start 5 sec loop")
      sleep(5)
      println("end 5 sec loop")
    dispatch_group_leave(self.longerQueue)
  }

}

2 个答案:

答案 0 :(得分:2)

这里有两类问题:

  1. 您正在<{1}}和longFunc内输入内的调度组。但是,由于您已将对这些功能的呼叫分派到各自的队列,因此您无法保证在到达您执行的longerFunc呼叫之前,您将会dispatch_group_enter到达dispatch_group_notify主线程上的viewDidLoad。还要记住,调度到后台队列的代码是异步运行的,我们无法保证它在运行时与在主线程上继续执行的代码相比。关键是,如果在您进入群组之前遇到dispatch_group_notify,则通知关闭将立即触发。

    要避免这种竞争条件,您要么在将代码发送到后台队列之前执行dispatch_group_enter中的viewDidLoad(显然,在设置通知结束之前),或者您想要退出群组的进入和退出,然后将dispatch_async替换为dispatch_group_async。这些方法中的任何一种都可以确保在将通知闭包添加到这些组之前输入组。

  2. 发送通知关闭,报告&#34; BETWEEN FUNCTIONS等待更长时间&#34;是一个更加令人震惊的例子,甚至没有竞争条件。您只需在调度稍后将进入组的函数之前添加该通知闭包。因此,在将代码分派到该队列之前,这几乎肯定会触发。

答案 1 :(得分:0)

使用dispatch_async对块进行排队时,可能需要一段时间才能开始运行该块,在此期间,排队线程(在您的情况下为主线程)继续运行。然后,排队的块可能会在排队线程中的下一个语句运行之前立即开始运行。

当您致电dispatch_group_notify时,如果该组的条目计数在此时为零,dispatch_group_notify将立即将通知块排队等待执行。排队执行块与运行块不同!如果调度队列繁忙,则不会立即运行通知块。

让我们看看(我推断)发生的事情是如何产生你的实际输出的。在每个具有重要影响的陈述之后,我将描述它的作用。我还会以粗体显示打印操作。

  override func viewDidLoad() {
    super.viewDidLoad()

    println("BEFORE FUNCTIONS")

该行将BEFORE FUNCTIONS打印到标准输出。

    dispatch_async(queueHigh) {
      self.longFunc()
    }

您将longFunc排队等候queueHigh。它立即开始运行并在dispatch_group_enter上调用longQueue,将longQueue的条目数提高到1. 它还打印了start 3 sec loop然后它进入睡眠状态3秒钟。它在后台线程上,所以这并没有阻止主线程。

    dispatch_group_notify(longQueue, dispatch_get_main_queue()) {
      println("BETWEEN FUNCTIONS WAIT ON LONG")
    }

BETWEEN FUNCTIONS WAIT ON LONG的条目数为零时,您提交了一个通知栏以打印longQueue。它现在不为零,因为longFunc已输入但尚未离开,因此通知块将添加到组中,而不是添加到主队列。

    dispatch_group_notify(longerQueue, dispatch_get_main_queue()) {
      println("BETWEEN FUNCTIONS WAIT ON LONGER")
    }

BETWEEN FUNCTIONS WAIT ON LONGER的条目数为零时,您提交了一个通知栏以打印longerQueue。它现在为零,因为longerFunc尚未排队,因此通知块会立即添加到主队列中。但主线程现在忙于运行此方法(viewDidLoad),因此它无法排空主队列。

    dispatch_async(queueLow) {
      self.longerFunc()
    }

您已将longerFunc提交至queueLow。它不会立即开始运行。

    dispatch_group_notify(longQueue, dispatch_get_main_queue()) {
      println("AFTER FUNCTIONS WAIT ON LONG")
    }

AFTER FUNCTIONS WAIT ON LONG的条目数为零时,您提交了一个通知栏以打印longQueue。它现在不为零,因为longFunc已输入但尚未离开,因此通知块将添加到组中,而不是主队列。

    dispatch_group_notify(longerQueue, dispatch_get_main_queue()) {
      println("AFTER FUNCTIONS WAIT ON LONGER")
    }

AFTER FUNCTIONS WAIT ON LONGER的条目数为零时,您提交了一个通知栏以打印longerQueue。它现在为零,因为longerFunc尚未启动,因此通知块会立即添加到主队列中。但主线程现在忙于运行此方法(viewDidLoad),因此它无法排空主队列。

  }

您从viewDidLoad返回,这让主线程继续执行主运行循环。这是接下来发生的事情:

  • longerFunc开始投放了。它在dispatch_group_enter上调用了longerQueue,将longerQueue的条目数提高到1.请注意,这不会从主队列中追溯删除这些通知块!他们在longerQueue的条目数为零时排队,并且无论longerQueue条目发生什么,它们都将保留在主队列中直到它们运行稍等一下。

  • longerFunc打印start 5 sec loop ,然后进入睡眠状态5秒钟。这是在它自己的后台线程中,所以它不会阻塞主线程或运行longFunc的线程。

  • 主线程的运行循环将一个块从主队列中取出并执行它。这是您为longerQueue提交的第一个通知块。 已打印BETWEEN FUNCTIONS WAIT ON LONGER

  • 主线程的运行循环将另一个块从主队列中取出并执行它。这是您为longerQueue提交的第二个通知栏。 已打印AFTER FUNCTIONS WAIT ON LONGER

  • 主线程的运行循环没有找到等待主队列的更多块。它进入了睡眠状态。

  • longFunc功能完成了3秒睡眠。 已打印end 3 sec loop

  • longFunc上的dispatch_group_leave函数longQueue。这会将longQueue的条目数减少到零,因此该组将所有待处理的通知块提交到主队列。这个行为唤醒了主线程的运行循环。

  • 主线程的运行循环将另一个块从主队列中取出并执行它。这是您为longQueue提交的第一个通知块。 已打印BETWEEN FUNCTIONS WAIT ON LONG

  • 主线程的运行循环将另一个块从主队列中取出并执行它。这是您为longQueue提交的第二个通知栏。 已打印AFTER FUNCTIONS WAIT ON LONG

  • 主线程的运行循环没有找到等待主队列的更多块。它进入了睡眠状态。

  • longerFunc功能完成了5秒睡眠。 已打印end 5 sec loop

  • longerFunc上的dispatch_group_leave函数longerQueue。这将longerQueue的条目数减少到零。 longerQueue组没有待处理的通知块,因此它什么也没做。

结束。