2

我一直在使用“usleep”在几毫秒内停止线程,并且我检查过它停止的时间比预期的要长。

我确定我做错了什么,我不是 Swift 专家,但我不明白它,因为它很容易检查。例如:

DispatchQueue.global(qos: .background).async {
    let timeStart: Int64 = Date().toMillis()
    usleep(20 * 1000) // 20 ms
    let timeEnd: Int64 = Date().toMillis()
    let timeDif = timeEnd - timeStart
    print("start: \(timeStart), end: \(timeEnd), dif: \(timeDif)")
}

结果:

start: 1522712546115, end: 1522712546235, dif: 120

如果我在主线程中执行相同的操作:

start: 1522712586996, end: 1522712587018, dif: 22

我认为我生成线程的方式对于停止它是错误的。我怎样才能生成一个与 usleep 配合得很好的线程?

谢谢

4

2 回答 2

2

几个想法:

  1. 响应性是队列服务质量的usleep函数。例如,对五种队列类型进行 30 次 20 毫秒的调用,得到以下平均值和标准偏差(以毫秒为单位):usleep

    QoS 平均标准差
    ———————————————————————————
    背景 99.14 28.06
    实用程序 74.12 23.66
    默认 23.88 1.83
    用户发起 22.09 1.87
    用户交互 20.99 0.29
    

    服务质量越高,它得到的时间越接近 20 毫秒,并且标准偏差越小。

  2. 如果您想要准确的时间测量,您应该避免使用Date和/或CFAbsoluteTimeGetCurrent()。正如文档警告我们的那样:

    重复调用此函数并不能保证结果单调递增。由于与外部时间参考同步或由于用户明确更改时钟,系统时间可能会减少。

    您可以使用mach_time基于值(例如由 方便地返回CACurrentMediaTime())来避免此问题。例如:

    let start = CACurrentMediaTime()
    // do something
    let elapsed = CACurrentMediaTime() - start
    
  3. 如果您需要更高的准确度,请参阅 Apple Technical Q&A #2169

于 2018-04-03T20:44:40.987 回答
0

在后台队列上休眠线程时,我也得到大约 120 毫秒的时间:

import Foundation

DispatchQueue.global(qos: .background).async {
    let timeStart = Date()
    usleep(20 * 1000) // 20 ms
    let timeEnd = Date()
    let timeDif = timeEnd.timeIntervalSince(timeStart) * 1000
    print("start: \(timeStart), end: \(timeEnd), dif: \(timeDif)")
    exit(0)
}

CFRunLoopRun()

输出:

start: 2018-04-03 00:10:54 +0000, end: 2018-04-03 00:10:54 +0000, dif: 119.734048843384

但是,使用默认 QoS,我的结果始终接近 20 毫秒:

import Foundation

DispatchQueue.global(qos: .default).async {
    let timeStart = Date()
    usleep(20 * 1000) // 20 ms
    let timeEnd = Date()
    let timeDif = timeEnd.timeIntervalSince(timeStart) * 1000
    print("start: \(timeStart), end: \(timeEnd), dif: \(timeDif)")
    exit(0)
}

CFRunLoopRun()

start: 2018-04-03 00:12:15 +0000, end: 2018-04-03 00:12:15 +0000, dif: 20.035982131958

因此,QoS 似乎.background导致了您所看到的行为。虽然我不直接知道为什么会这样,但推测操作系统在唤醒标记有后台 QoS 的睡眠线程方面可能会有些松懈,这似乎并不牵强。事实上,这就是Apple 的文档所要说的:

服务质量 (QoS) 类允许您对要由 NSOperation、NSOperationQueue、NSThread 对象、调度队列和 pthread(POSIX 线程)执行的工作进行分类。通过将 QoS 分配给工作,您可以表明其重要性,并且系统会对其进行优先级排序并相应地对其进行调度。例如,系统比可以推迟到更佳时间的后台工作更早地执行用户发起的工作。在某些情况下,系统资源可能会从优先级较低的工作中重新分配并分配给优先级较高的工作。

您的背景工作被“推迟到更佳时间”的可能性似乎是对您所看到的行为的合理解释。

于 2018-04-03T00:16:04.417 回答