【问题标题】:RunLoopObserver and RunLoopActivityRunLoop 观察者和 RunLoop 活动
【发布时间】:2016-12-19 23:59:22
【问题描述】:

在下面显示的代码中,我创建了一个线程,它创建 0 到 15 之间的随机数,并在它出现 3 时停止,更改结束参数。在我向主线程的运行循环添加了一个运行循环观察者(即“观察”结束参数)之后。如您所见,运行循环观察者和我的线程在打印前都休眠了 1 秒,所以我希望在控制台中,观察者的打印和我的线程的打印是交替的。事实并非如此。我相信,如果我理解它,它将取决于CFrunloopActivity 参数及其可能的组合。

谁能解释一下这个参数的操作? 如果是,是否有交替打印的组合?如果不能交替打印,那么观察者如何在主线程的运行循环中工作?

谢谢

这是代码:

class ViewController: UIViewController {
    var end = false

    override func viewDidLoad() {
        super.viewDidLoad()

        //my thread
        performSelector(inBackground: #selector(rununtil3(thread:)), with: Thread.current)

        //the observer
        let runLoopObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.entry.rawValue | CFRunLoopActivity.exit.rawValue , true , 0 , {

        (observer: CFRunLoopObserver?, activity: CFRunLoopActivity) -> Void in

        Thread.sleep(until: Date(timeIntervalSinceNow: 1))
        print("+++ is main?: \(Thread.isMainThread)")
        if self.end == true {
            //print the end of my thread and remove the observer from main run loop
            print("end of own thread")
            CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, CFRunLoopMode.commonModes)
            return
        }

        //CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, CFRunLoopMode.commonModes)

        })
        //add observer to main run loop
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), runLoopObserver, CFRunLoopMode.commonModes)

        print("Out of observer")

     }

    func rununtil3(thread : Thread) {
    print("main?: \(thread.isMainThread) is run : \(thread.isExecuting)")
        while true {
            let ran = Int (arc4random() % 15 )
            Thread.sleep(until: Date(timeIntervalSinceNow: 1))
            print("\(ran) . main is run : \(thread.isExecuting)")
            if ran == 3 {
                end = true
                Thread.exit()
            }
        }
    }
}

【问题讨论】:

    标签: swift multithreading runloop


    【解决方案1】:

    您正在创建一个 runloop 观察者并要求在 runloop 开始操作(其enter 事件)或exits 时得到通知。 Runloop 活动记录在 here

    enter 事件在由CFRunLoopRun() 或类似启动运行循环时传递。如果你手动创建一个runloop,添加一个enter观察者,然后在你的新runloop上调用CFRunLoopRun(),你会收到一个enter事件。如果您稍后在 runloop 上调用 CFRunLoopStop(),您将收到 exit 事件。

    当您将enter 观察者添加到已经运行 的runloop 时,您将收到enter 事件。这是为了让您的观察者状态与运行循环的实际状态保持一致。

    在您的代码中,您创建一个运行循环观察器,然后将其附加到主线程的运行循环(也称为“主运行循环”)。

    操作系统会在程序启动时自动为您创建 Main Runloop,并自动在其上调用CFRunLoopRun()CFRunLoopStop() 永远不会被调用,因此主 runloop 有效地永远运行。

    由于主运行循环已经在运行,您会收到一个enter 事件。由于主运行循环不会停止,因此您永远不会看到 exit 事件。

    请务必注意,runloop 观察者绑定到您添加到的特定 runloop,而不是某个任意后台线程或属性的生命周期(即您的 end 属性不是被观察的东西)。


    关于如何让线程交替出现的第二个问题,这是一个答案非常广泛的问题,很大程度上取决于您想要做什么。在这里我不会一一回答,只是给你一些想法。

    1. 最好不要创建后台线程,而是将计时器添加到每秒触发的主运行循环中。然后你会得到周期性的行为。

    2. 如果您真的想使用后台线程,那么您应该阅读一本关于线程通信和同步的优秀操作系统书籍。在 iOS/OS X 上常见的做法是使用后台线程,然后使用 DispatchQueue.main.async { } 之类的东西向主线程发信号通知您的处理已完成。如果你稍微搜索一下,有很多例子可以说明如何做到这一点。

    3. 您还可以阅读有关使用信号量或条件变量进行线程同步的内容。你肯定想做的一件事是在主线程上调用Thread.sleep(),就像你在观察者回调中所做的那样。如果您在主线程上等待的时间过长,操作系统将杀死您的应用程序。最好让后台线程完全独​​立于主线程,然后使用DispatchQueue 调用回调到主线程,正如我在上面#2 中提到的那样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-11
      • 2020-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多