gpt4 book ai didi

ios - 如何在for循环中暂停调度队列?

转载 作者:行者123 更新时间:2023-12-01 16:01:32 27 4
gpt4 key购买 nike

我有播放和暂停按钮。当我按下播放按钮时,我想在循环中播放异步对话。我使用调度组在 for 循环内等待异步方法。但我无法实现暂停。

startStopButton.rx.tap.bind {
if self.isPaused {
self.isPaused = false
dispatchGroup.suspend()
dispatchQueue.suspend()
} else {
self.isPaused = true
self.dispatchQueue.async {
for i in 0..<self.textBlocks.count {
self.dispatchGroup.enter()
self.startTalking(string: self.textBlocks[i]) { isFinished in
self.dispatchGroup.leave()
}
self.dispatchGroup.wait()
}
}
}
}.disposed(by: disposeBag)

我尝试使用 operationqueue 但仍然无法正常工作。它还在继续说话。

startStopButton.rx.tap.bind {
if self.isPaused {
self.isPaused = false
self.talkingQueue.isSuspended = true
self.talkingQueue.cancelAllOperations()
} else {
self.isPaused = true
self.talkingQueue.addOperation {
for i in 0..<self.textBlocks.count {
self.dispatchGroup.enter()
self.startTalking(string: self.textBlocks[i]) { isFinished in
self.dispatchGroup.leave()
}
self.dispatchGroup.wait()
}
}
}
}.disposed(by: disposeBag)

有什么建议吗?

最佳答案

一些观察:

  1. 暂停群组没有任何作用。您暂停队列,而不是群组。

  2. 挂起队列会阻止新项目在该队列上启动,但不会挂起该队列上已经运行的任何内容。因此,如果您已将所有 textBlock 调用添加到单个分派(dispatch)的工作项中,那么一旦启动,它就不会挂起。

    因此,与其将所有这些文本 block 作为单个任务分派(dispatch)到队列中,不如单独提交它们(当然,假设您的队列是串行的)。因此,例如,假设您有一个 DispatchQueue:

    let queue = DispatchQueue(label: "...")

    然后,为了对任务进行排队,将 async 调用放在 for 循环中,因此每个文本 block 都是一个单独的项目你的队列:

    for textBlock in textBlocks {
    queue.async { [weak self] in
    guard let self = self else { return }

    let semaphore = DispatchSemaphore(value: 0)

    self.startTalking(string: textBlock) {
    semaphore.signal()
    }
    semaphore.wait()
    }
    }

    仅供引用,虽然调度组有效,但信号量(非常适合协调单个 signalwait)在这里可能是更合乎逻辑的选择,而不是组(用于协调分派(dispatch)任务组)。

    无论如何,当您挂起该队列时,该队列将阻止启动任何排队的内容(但会完成当前的 textBlock)。

  3. 或者您可以使用异步Operation,例如,创建您的队列:

    let queue: OperationQueue = {
    let queue = OperationQueue()
    queue.name = "..."
    queue.maxConcurrentOperationCount = 1
    return queue
    }()

    然后,您再次将每个说出的单词排队,每个单词分别是该队列上的一个单独操作:

    for textBlock in textBlocks {
    queue.addOperation(TalkingOperation(string: textBlock))
    }

    这当然假设您将谈话例程封装在一个操作中,例如:

    class TalkingOperation: AsynchronousOperation {
    let string: String

    init(string: String) {
    self.string = string
    }

    override func main() {
    startTalking(string: string) {
    self.finish()
    }
    }

    func startTalking(string: String, completion: @escaping () -> Void) { ... }
    }

    我更喜欢这种方法,因为

    • 我们不会阻塞任何线程;
    • 本着单一责任原则的精神,谈话的逻辑很好地封装在 TalkingOperation 中;和
    • 您可以轻松暂停队列或取消所有操作。

    顺便说一句,这是 AsynchronousOperation 的子类,它从 TalkingOperation 类中抽象出异步操作的复杂性。有很多方法可以做到这一点,但这里是 one random implementation . FWIW,想法是定义一个 AsynchronousOperation 子类,该子类执行 documentation 中概述的异步操作所需的所有 KVO。 ,然后您就可以享受操作队列的好处,而不必让每个异步操作子类都太复杂。

  4. 就其值(value)而言,如果您不需要挂起,但很高兴只是取消,另一种方法是将整个 for 循环作为单个工作项或操作进行分派(dispatch), 但检查操作是否已在 for 循环内取消:

    因此,定义一些属性:

    let queue = DispatchQueue(label: "...")
    var item: DispatchWorkItem?

    然后就可以开始任务了:

    item = DispatchWorkItem { [weak self] in
    guard let textBlocks = self?.textBlocks else { return }

    for textBlock in textBlocks where self?.item?.isCancelled == false {
    let semaphore = DispatchSemaphore(value: 0)
    self?.startTalking(string: textBlock) {
    semaphore.signal()
    }
    semaphore.wait()
    }
    self?.item = nil
    }

    queue.async(execute: item!)

    然后,当您想停止它时,只需调用 item?.cancel()。您也可以使用非异步 Operation 执行相同的模式。

关于ios - 如何在for循环中暂停调度队列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59268619/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com