如何打破内部使用异步 DispatchQueue 的循环
How to break out of a loop that uses async DispatchQueue inside
我正在使用 for 循环和带有异步功能的 DispatchQueue,以在 5 或 10 分钟的持续时间内逐渐增加播放量。
我目前的实现方式是:
for i in (0...(numberOfSecondsToFadeOut*timesChangePerSecond)) {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)/Double(timesChangePerSecond)) {
if self.activityHasEnded {
NSLog("Activity has ended") //This will keep on printing
} else {
let volumeSetTo = originalVolume - (reductionAmount)*Float(i)
self.setVolume(volumeSetTo)
}
}
if self.activityHasEnded {
break
}
}
我的目标是让 activityHasEnded
充当断路器。评论中指出的问题是,尽管使用 break
,但 NSLog
将在每个时期继续打印。完全摆脱使用 DispatchQueue.main.asyncAfter
的 for 循环的更好方法是什么?
更新:如 Rob 所述,使用 Timer
更有意义。这是我所做的:
self.fadeOutTimer = Timer.scheduledTimer(withTimeInterval: timerFrequency, repeats: true) { (timer) in
let currentVolume = self.getCurrentVolume()
if currentVolume > destinationVolume {
let volumeSetTo = currentVolume - reductionAmount
self.setVolume(volumeSetTo)
print ("Lowered volume to \(volumeSetTo)")
}
}
当不再需要计时器时,我调用self.fadeOutTimer?.invalidate()
您可以使用 DispatchWorkItem
,它可以异步发送到 DispatchQueue
,也可以在发送后取消。
for i in (0...(numberOfSecondsToFadeOut*timesChangePerSecond)) {
let work = DispatchWorkItem {
if self.activityHasEnded {
NSLog("Activity has ended") //This will keep on printing
} else {
let volumeSetTo = originalVolume - (reductionAmount)*Float(i)
self.setVolume(volumeSetTo)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)/Double(timesChangePerSecond), execute: work)
if self.activityHasEnded {
work.cancel() // cancel the async work
break // exit the loop
}
}
您不想使用 asyncAfter
:虽然您可以使用 DispatchWorkItem
再现(可取消),但您最终会在尝试跟踪所有个体时陷入混乱工作项目。更糟糕的是,一系列单独调度的项目将受到“计时器合并”的影响,其中后面的任务将开始聚集在一起,不再以所需的间隔触发。
简单的解决方案是使用重复 Timer
,这样可以避免合并,并且在您想停止合并时很容易失效。
我正在使用 for 循环和带有异步功能的 DispatchQueue,以在 5 或 10 分钟的持续时间内逐渐增加播放量。
我目前的实现方式是:
for i in (0...(numberOfSecondsToFadeOut*timesChangePerSecond)) {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)/Double(timesChangePerSecond)) {
if self.activityHasEnded {
NSLog("Activity has ended") //This will keep on printing
} else {
let volumeSetTo = originalVolume - (reductionAmount)*Float(i)
self.setVolume(volumeSetTo)
}
}
if self.activityHasEnded {
break
}
}
我的目标是让 activityHasEnded
充当断路器。评论中指出的问题是,尽管使用 break
,但 NSLog
将在每个时期继续打印。完全摆脱使用 DispatchQueue.main.asyncAfter
的 for 循环的更好方法是什么?
更新:如 Rob 所述,使用 Timer
更有意义。这是我所做的:
self.fadeOutTimer = Timer.scheduledTimer(withTimeInterval: timerFrequency, repeats: true) { (timer) in
let currentVolume = self.getCurrentVolume()
if currentVolume > destinationVolume {
let volumeSetTo = currentVolume - reductionAmount
self.setVolume(volumeSetTo)
print ("Lowered volume to \(volumeSetTo)")
}
}
当不再需要计时器时,我调用self.fadeOutTimer?.invalidate()
您可以使用 DispatchWorkItem
,它可以异步发送到 DispatchQueue
,也可以在发送后取消。
for i in (0...(numberOfSecondsToFadeOut*timesChangePerSecond)) {
let work = DispatchWorkItem {
if self.activityHasEnded {
NSLog("Activity has ended") //This will keep on printing
} else {
let volumeSetTo = originalVolume - (reductionAmount)*Float(i)
self.setVolume(volumeSetTo)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)/Double(timesChangePerSecond), execute: work)
if self.activityHasEnded {
work.cancel() // cancel the async work
break // exit the loop
}
}
您不想使用 asyncAfter
:虽然您可以使用 DispatchWorkItem
再现(可取消),但您最终会在尝试跟踪所有个体时陷入混乱工作项目。更糟糕的是,一系列单独调度的项目将受到“计时器合并”的影响,其中后面的任务将开始聚集在一起,不再以所需的间隔触发。
简单的解决方案是使用重复 Timer
,这样可以避免合并,并且在您想停止合并时很容易失效。