- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有 iOS 应用程序,我正在使用 AVAudioEngine 播放音频。我将音频文件中的帧读取到 AVAudioPCMBuffer 中,然后将缓冲区发布到 AVAudioPlayerNode.scheduleBuffer 中。我在 DispatchQueue 上读取帧。我遇到的问题是,当应用程序有很多其他工作要做时 - 与服务器同步和下载文件,读取 DispatchQueue 上的帧会延迟几秒钟,这会导致声音中断。我已将 DispatchQueueQos 设置为尽可能高的 .userInteractive,并且应用程序中没有具有相同 qos 的其他队列,但有时仍需要几秒钟才能在该队列上执行代码。
有什么办法可以解决这个问题吗?有什么方法可以告诉操作系统这个队列用于音频或类似的东西吗?
注意:如果应用程序运行一些繁重的后台操作,我的 AVPlayer 不会出现此问题。
编辑:一些代码以便更好地理解。问题是有时需要几秒钟的时间才能将任务发布到队列上(从评论 1 到评论 2)。
var audioProcessingQueue = DispatchQueue(label: "audioProcessing", qos: .userInteractive)
var player = AVAudioPlayerNode()
//comment 1
self.audioProcessingQueue.async(flags: .barrier) {
//comment 2
//some buffer processing here...
player.scheduleBuffer(buffer, at: nil)
}
谢谢
最佳答案
有两件事:
1) 您提供的代码安排在哪个队列上?因为听起来该队列可能与您的下载任务共享其服务质量。分派(dispatch)到另一个队列确实需要多个步骤。因此,仅仅因为“comment1”被传递,其他队列之一可能会在您调度时获得线程时间来完成一些工作。这会导致一些延迟,是的,而且似乎是最可能的原因。
2) WWDC 2016 视频 Concurrent Programming with GCD in Swift 3 , 可能有帮助。如果 cpu 的所有核心都忙于其他任务,则将新任务分派(dispatch)到更高优先级队列不会立即自动启动该任务。 GCD 会尽力而为,但也许您在其他队列上的任务非常紧张/做一些中断会导致问题的事情。
因此,如果您有必须立即启动的任务,请确保有一个 CPU 核心/线程处于空闲状态。我会尝试使用 OperationQueue
并设置其 maxConcurrentOperationCount
。然后将所有下载操作放入该操作队列中,留下核心/线程准备启动音频。
但是我认为选项 2 在这里无效,因为你说它有时需要几秒钟。
更新:
正如您所说,您提供的代码在一个队列上运行,该队列也用于您的下载任务,这主要是一个错误的架构设计,我很遗憾地说,没有任何人可以想出解决方案查看整个项目以帮助更好地构建它。您必须弄清楚何时以及将哪些任务分派(dispatch)给哪个优先级。
以下是我对您提供的代码的想法:
为什么要使用.barrier
标志?此标志可确保队列不会完成其他并发任务,如果您的数据由多个任务操作并且希望避免“竞争条件”,那么该标志非常有用。然而,这也意味着在您调度该 block 之后,它将等待在它之前调度的其他任务,然后阻塞队列来执行您的任务。
正如其他人评论的那样,使用工具分析您的代码可能会显示此行为。
以下是我如何构建您的音频请求:
//A serial (not concurrent) audio queue. Will download one data at a time and play audio
let audioQueue = DispatchQueue(label: "Audio Queue My App", qos: .userInitiated)
//I'm on the main queue here!
audioQueue.async {
//I'm on the audio queue here, thus not blocking the main thread
let result = downloadSomeData()
//Download is done here, and I'm not being blocked by other dispatch queues if their priority is lower.
switch result {
case .success(let data):
//Do something with the data and play audio
case .failure(let error):
//Handle Error
}
}
///Downloads some audio data
func downloadSomeData() -> Swift.Result<Data, Error> {
//Create dispatch group
let dg = DispatchGroup()
dg.enter()
///my audio data
var data: Data?
//Dispatch onto a background queue OR place a block operation onto your OperationQueue
DispatchQueue.global(qos: .background).async {
//Download audio data... Alamofire/ URLSession
data = Data()
dg.leave()
}
dg.wait()
//Data was downloaded! We're back on the queue that called us.
if let nonOptionalData = data {
return .success(nonOptionalData)
} else {
return .failure(NSError(domain: "MyDomain", code: 007, userInfo: [ NSLocalizedDescriptionKey: "No data downloaded, failed?"]))
}
}
关于选项#2:
可以使用ProcessInfo().activeProcessorCount
检索事件核心的数量。请注意,理论上,该值可能会在运行应用程序时由于热限制或其他原因而发生变化。通过使用此信息并将 OperationQueue
的 maxConcurrentOperationCount
设置为等于事件处理器的数量,您可以确保操作/任务不必共享 CPU 时间。 (只要没有其他 DispatchQueue 正在运行。)然而这种方法并不安全。所需要的只是团队成员将来将任务分派(dispatch)到其他地方。
但是,此信息的有用之处在于,您可以通过使用专用的操作队列(例如 2 个最大并发操作)并将所有后台工作放在此 DispatchQueue 上来限制下载任务使用的资源量。您的 CPU 很可能不会充分发挥其潜力并留下一些空间。
关于ios - 如何拥有高优先级线程进行音频处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57097561/
我是一名优秀的程序员,十分优秀!