3

在下面显示的代码中,我创建了一个线程,它创建 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()
            }
        }
    }
}
4

1 回答 1

3

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

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

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

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

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

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

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


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

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

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

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

于 2016-12-20T01:50:54.303 回答