如何防止线程打印在控制台中混合输出?

时间:2017-11-03 22:15:50

标签: ios swift multithreading logging swift4

假设我们有多个线程发出打印件。 通常在下载内容时如下:

let url = self.url
print("loadPreview(\(source) for \(url)): ↝start loading \(self.url")
let task = session.downloadTask(with: url) { 
    (localUrl, response, error) in
    print("loadPreview(\(source) for \(url)): == \(self.url")
}

有没有办法让print原子并阻止输出如下?

loadPreview(WSJ for www.wsj.co⟷TloadPreview(9loadPreview(appleins   for appleinsid⟷n-messages):     ↝start loading http://app⟷n-messages

2 个答案:

答案 0 :(得分:1)

快速破解就是在打印件周围使用NSLock。例如,您可以将原子打印函数定义为:

private let printLock = NSLock()

func aprint(_ message: String){
    printLock.lock()
    defer { printLock.unlock() }
    print(message)
}

并像标准print函数一样使用它:

aprint(“This will print atomically!”)

您还可以使用序列DispatchQueue 序列化打印调用来获得类似的结果。例如:

private let printQueue = DispatchQueue(label: "aprint", qos: .utility)

func aprint(_ message: String){
    printQueue.async {
        print(message)
    }
}

此解决方案提供了更好的性能,主要归功于async调用。这可确保调用线程阻塞,直到获取锁定(并且完全执行相应的print)。但是,很明显,这个解决方案也是完全原子的。

(对于记录,如果调用代码在主队列上运行,那么简单地使用DispatchQueue.main可能会表现得很奇怪。)

我建议选择第二种解决方案;)

答案 1 :(得分:0)

您可以使用DispatchIO以非阻塞方式将输出自动序列化为stdout或文件。还有更多工作,您可能需要将以下示例包装到一个类中:

import Foundation

// Open stdout with DispatchIO
let queue = DispatchQueue.global()
let fd = FileHandle.standardOutput.fileDescriptor
let writer = DispatchIO(type: .stream, fileDescriptor: fd, queue: queue) { (errno) in
  if errno != 0 {
    print("Cannot open stdout: \(errno)")
  }
}

// Encode string
if let data = string.data(using: .utf8) {
  data.withUnsafeBytes { (buffer) in
    // Produce DispatchData with encoded string
    let dispatchData = DispatchData(bytes: buffer)

    // Print string to stdout
    writer.write(offset: .zero, data: dispatchData, queue: queue) { (done, data, errno) in
      if errno != 0 {
        print("Write error: \(errno)")
      }
    }
  }
}

// Close the DispatchIO when you don't need it anymore
writer.close()