多线程经典题目,如何让两个线程交替输出?
就像这样:
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
如果想要实现这样的效果,那么线程是不能被释放的,因为线程一旦释放,再次异步执行的时候就会创建新线程,不符合题目要求,所以结合线程保活,只能用runLoop 来实现。
先看下 RunLoop 都有哪些启动方法:
//Puts the receiver into a permanent loop, during which time it processes data from all attached input sources.
open func run()
//Runs the loop until the specified date, during which time it processes data from all attached input sources.
open func run(until limitDate: Date)
//Runs the loop once, blocking for input in the specified mode until a given date.
open func run(mode: RunLoop.Mode, before limitDate: Date) -> Bool
//Runs the loop once or until the specified date, accepting input only for the specified mode.
open func acceptInput(forMode mode: RunLoop.Mode, before limitDate: Date)
这三种方式无论通过哪一种方式启动runloop,如果没有一个输入源或者timer附加于runloop上,runloop就会立刻退出。
(1) 第一种方式,runloop会一直运行下去,在此期间会处理来自输入源的数据,并且会在NSDefaultRunLoopMode模式下重复调用runMode:beforeDate:方法;
(2)第二种方式,runloop会在指定时间之前一直运行,在此期间会处理来自输入源的数据,并且会在NSDefaultRunLoopMode模式下重复调用runMode:beforeDate:方法;
(3) 第三种方式,可以设置超时时间,在超时时间到达之前,runloop会一直运行,在此期间runloop会处理来自输入源的数据,并且也会在NSDefaultRunLoopMode模式下重复调用runMode:beforeDate:方法;
(4) 第四种方式,runloop会运行一次,超时时间到达或者第一个input source被处理,则runloop就会退出。
退出Runloop 的方式
与开启runloop 的方式对应
- 如果想退出runloop,不应该使用第一种启动方式来启动runloop。
如果runloop没有input sources或者附加的timer,runloop就会退出。
虽然这样可以将runloop退出,但是苹果并不建议我们这么做,因为系统内部有可能会在当前线程的runloop中添加一些输入源,所以通过手动移除input source或者timer这种方式,并不能保证runloop一定会退出。 - 第二种和第三种方式可以通过设置超时时间来退出runloop。
手动退出runloop
@objc func endAllSubRunLoop() {
debugPrint("\(Thread.current) end!!!")
CFRunLoopStop(CFRunLoopGetCurrent())
}
如何实现两个线程交替输出
self.thread1 = Thread(target: self, selector: #selector(startSubThread), object: nil)
self.thread1.start()
self.thread2 = Thread(target: self, selector: #selector(startSubThread), object: nil)
self.thread2.start()
@objc func startSubThread() {
let runLoop = RunLoop.current
runLoop.add(Port(), forMode: .default)
runLoop.run(until: .distantFuture)
debugPrint("runLoop 启动 at: \(Thread.current)")
}
@IBAction func beginCrossAction(_ sender: Any) {
self.endRunLoop = false
self.perform(#selector(subThread1Action), on: self.thread1, with: nil, waitUntilDone: false)
}
@IBAction func endCrossAction(_ sender: Any) {
self.endRunLoop = true
}
@objc func endAllSubRunLoop() {
debugPrint("\(Thread.current) end!!!")
CFRunLoopStop(CFRunLoopGetCurrent())
}
@objc func subThread1Action() {
if endRunLoop {
self.endAllSubRunLoop()
} else {
print("\(Thread.current)thred acion doing log with flag 1")
self.perform(#selector(subThread2Action), on: self.thread2, with: nil, waitUntilDone: false)
}
}
@objc func subThread2Action() {
if endRunLoop {
self.endAllSubRunLoop()
} else {
print("\(Thread.current)thred acion doing log with flag 2")
self.perform(#selector(subThread1Action), on: self.thread1, with: nil, waitUntilDone: false)
}
}
输出如下
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2
{number = 6, name = (null)}thred acion doing log with flag 1
{number = 7, name = (null)}thred acion doing log with flag 2