问题
I am writing a MacOS/Cocoa app that monitors a remote log file using a common recipe that launches a Process
(formerly NSTask
) instance on a background thread and reads stdout
of the process via a Pipe
(formally a NSPipe
) as listed below:
class LogTail {
var process : Process? = nil
func dolog() {
//
// Run ssh fred@foo.org /usr/bin/tail -f /var/log.system.log
// on a background thread and monitor it's stdout.
//
let processQueue = DispatchQueue.global(qos: .background)
processQueue.async {
//
// Create process and associated command.
//
self.process = Process()
process.launchPath = "/usr/bin/ssh"
process.arguments = ["fred@foo.org",
"/usr/bin/tail", "-f",
"/var/log.system.log"]
process.environment = [ ... ]
//
// Create pipe to read stdout of command as data is available
//
let pipe = Pipe()
process.standardOutput = pipe
let outHandle = pipe.fileHandleForReading
outHandle.readabilityHandler = { pipe in
if let string = String(data: pipe.availableData,
encoding: .utf8) {
// write string to NSTextView on main thread
}
}
//
// Launch process and block background thread
// until process complete.
//
process.launch()
process.waitUntilExit()
//
// What do I do here to make sure all related
// threads terminate?
//
outHandle.closeFile() // XXX
outHandle.readabilityHandler = nil // XXX
}
}
Everything works just dandy, but when the process quits (killed via process.terminate
) I notice (via Xcode's Debug Navigator and the Console app) that there are multiple threads consuming 180% or more of the CPU!?!
Where is this CPU leak coming from?
I threw in outHandle.closeFile()
(see code marked XXX above) and that reduced the CPU usage down to just a few percent but the threads still existed! What am I doing wrong or how do a make sure all the related threads terminate (I prefer graceful terminations i.e., threads body finish executing)!?
Some one posted a similar question almost 5 years ago!
UPDATE:
The documentation for NSFileHandle's readabilityHandler says:
To stop reading the file or socket, set the value of this property to nil. Doing so cancels the dispatch source and cleans up the file handle’s structures appropriately.
so setting outHandle.readabilityHandler = nil
seems to solve the problem too.
Even though I have seemingly solved the problem, I really don't understand where this massive CPU leak comes from -- very mysterious.
来源:https://stackoverflow.com/questions/44833266/how-to-terminate-all-threads-reading-from-pipe-nspipe-related-to-process-nsta