【问题标题】:How to suspend a work item on the main queue如何暂停主队列上的工作项
【发布时间】:2018-12-02 20:55:58
【问题描述】:

我想知道是否可以在保持“.asyncAfter”时间的同时暂停然后恢复主队列上的工作项。如果没有,是否有解决方法来实现这一点?

在某个时刻,我将以下 DispatchWorkItem 排队:

dispatchWorkItem = DispatchWorkItem(qos: .userInteractive, block: {
            self.view.backgroundColor = UIColor.workoutBackgroundColor
            self.runTimer()
            self.timerButton.animateableTrackLayer.removeAnimation(forKey: "strokeEndAnimation")
            self.isRestState = false
        })

我使用:

    DispatchQueue.main.asyncAfter(deadline: delayTime, execute: self.dispatchWorkItem))

(delayTime 是函数的参数)

现在,我遇到的问题是,如果用户在我的应用中执行“暂停”操作,我该如何暂停此工作项。

我已尝试使用DispatchQueue.main.suspend() 方法,但工作项在指定的延迟时间后继续执行。根据我的阅读,这个方法应该挂起队列和这个排队的工作项,因为它没有被执行。 (如果我错了,请纠正我!)

我需要实现的是工作项“暂停”,直到用户在应用程序中执行“恢复”操作,这将从延迟时间停止的地方恢复工作项。

这适用于我在不需要更新 UI 时创建的后台队列;但是,在主队列上似乎动摇了。

我考虑过的一种解决方法是,当用户执行暂停操作时,存储直到工作项将要执行的剩余时间,并在恢复操作的那个时间将工作项重新添加到队列中。这似乎是一种质量较差的方法,我觉得有一种更合适的方法。

在这方面,是否可以创建一个后台队列,在执行时执行主队列上的工作项?

提前致谢!

【问题讨论】:

  • @matt 我正在使用“asyncAfter”方法,该方法允许代码和用户与应用程序的交互继续进行,直到工作项被执行,当它被执行时,用户将无法执行暂停行动。
  • stackoverflow.com/questions/29492707/… 的可能副本,其中明确表示您不能挂起不属于您的队列(主队列也不是您的)。

标签: swift grand-central-dispatch


【解决方案1】:

在这方面,是否可以创建一个后台队列,在执行时执行主队列上的工作项?

您提出的建议是这样的:

var q = DispatchQueue(label: "myqueue")
func configAndStart(seconds:TimeInterval, handler:@escaping ()->Void) {
    self.q.asyncAfter(deadline: .now() + seconds, execute: {
        DispatchQueue.main.async(execute: handler())
    })
}
func pause() {
    self.q.suspend()
}
func resume() {
    self.q.resume()
}

但我的实际测试似乎表明这不会如你所愿;倒计时不会从暂停的地方恢复。

我考虑过的一种解决方法是,当用户执行暂停操作时,存储直到工作项将要执行的剩余时间,并在恢复操作的那个时间将工作项重新添加到队列中。这似乎是一种质量较差的方法,我觉得有一种更合适的方法。

质量不差。没有用于暂停调度计时器倒计时或内省计时器的内置机制,因此如果您想在主队列上完成所有事情,您唯一的办法就是您所说的:维护您自己的计时器和必要的状态变量。这是我蹒跚而行的一个相当愚蠢的模型:

class PausableTimer {
    var t : DispatchSourceTimer!
    var d : Date!
    var orig : TimeInterval = 0
    var diff : TimeInterval = 0
    var f : (()->Void)!
    func configAndStart(seconds:TimeInterval, handler:@escaping ()->Void) {
        orig = seconds
        f = handler
        t = DispatchSource.makeTimerSource()
        t.schedule(deadline: DispatchTime.now()+orig, repeating: .never)
        t.setEventHandler(handler: f)
        d = Date()
        t.resume()
    }
    func pause() {
        t.cancel()
        diff = Date().timeIntervalSince(d)
    }
    func resume() {
        orig = orig-diff
        t = DispatchSource.makeTimerSource()
        t.schedule(deadline: DispatchTime.now()+orig, repeating: .never)
        t.setEventHandler(handler: f)
        t.resume()
    }
}

这在我的粗略测试中有效,并且似乎可以根据需要中断(可暂停),但不要引用我的话;我没有花太多时间在上面。细节留给读者练习!

【讨论】:

  • 实际上我的测试表明第一种方法根本不起作用。我将其从我的答案中收回。
  • 我在测试时也遇到了同样的问题,我会继续使用计时方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-07
  • 2021-02-12
  • 1970-01-01
  • 2016-08-04
  • 2019-05-13
  • 1970-01-01
  • 2015-06-12
相关资源
最近更新 更多