如何停止调度队列中任务的执行?
Posted
技术标签:
【中文标题】如何停止调度队列中任务的执行?【英文标题】:How to stop the execution of tasks in a dispatch queue? 【发布时间】:2011-10-19 05:10:43 【问题描述】:如果我有一个串行队列,我如何从主线程告诉它立即停止执行并取消其所有任务?
【问题讨论】:
我这里是用例子回答的,你可以看看。 enter link description here 【参考方案1】:从 ios 9 / OS X 10.11 开始,如果不自己实现非平凡的逻辑,就无法从调度队列中清空待处理任务。
如果您需要取消调度队列,最好使用NSOperationQueue
,它提供了更多功能。例如,以下是“取消”队列的方法:
NSOperationQueue* queue = [NSOperationQueue new];
queue.maxConcurrentOperationCount = 1; // make it a serial queue
...
[queue addOperationWithBlock:...]; // add operations to it
...
// Cleanup logic. At this point _do not_ add more operations to the queue
queue.suspended = YES; // halts execution of the queue
[queue cancelAllOperations]; // notify all pending operations to terminate
queue.suspended = NO; // let it go.
queue=nil; // discard object
【讨论】:
有一些新的api,查看dispatch_block_t
,您可以使用dispatch_block_cancel
取消一个块【参考方案2】:
如果您使用Swift
,DispatchWorkItem
类允许单独取消工程单元。
工作项允许您直接配置各个工作单元的属性。它们还允许您处理单个工作单元,以等待其完成、收到有关其完成的通知和/或取消它们。 ( 适用于 iOS 8.0+ macOS 10.10+)。
DispatchWorkItem 封装了可以执行的工作。一个工作项 可以分派到 DispatchQueue 和 DispatchGroup 中。一种 DispatchWorkItem 也可以设置为 DispatchSource 事件, 注册,或取消处理程序。
↳https://developer.apple.com/reference/dispatch/dispatchworkitem
【讨论】:
使用箭头的好主意:) 谢谢,这正是我想要的!知道为什么该课程的文档如此之少吗?DispatchWorkItem
不会取消它已经开始执行的工作项。如果DispatchQueue
尚未执行,取消一个只会停止未来的执行。
@shoe:DispatchWorkItem
将用作DispatchGroup
中的取消处理程序。这允许当前正在执行的操作不断检查其状态并根据handier 取消,这实际上将其置于完成状态,停止进一步执行。
OP 要求一种方法来停止当前正在执行的任务。您发布了对他们问题的答案,但这不是他们问题的解决方案。因此,在 this 上下文中,您的答案具有误导性。 DispatchWorkItem
将 不取消正在执行的任务。 DispatchWorkItem
不提供该功能,即使它与 DispatchGroup
一起使用。【参考方案3】:
这是一个很常见的问题,我之前回答过一个问题:
Suspending GCD query problem
简短的回答是 GCD 没有取消 API;您必须自己实施取消代码。在上面的回答中,我基本上展示了如何做到这一点。
【讨论】:
【参考方案4】:详情
Xcode 版本 10.2 (10E125),Swift 5方式1.OperationQueue
OperationQueue Operation cancel() Operation and OperationQueue Tutorial in Swift取消操作对象会使对象留在队列中,但会通知对象它应该尽快停止其任务。对于当前正在执行的操作,这意味着操作对象的工作代码必须检查取消状态,停止正在做的事情,并将自己标记为已完成
解决方案
class ViewController: UIViewController
private lazy var queue = OperationQueue()
override func viewDidLoad()
super.viewDidLoad()
queue.addOperation(SimpleOperation(title: "Task1", counter: 50, delayInUsec: 100_000))
queue.addOperation(SimpleOperation(title: "Task2", counter: 10, delayInUsec: 500_000))
DispatchQueue .global(qos: .background)
.asyncAfter(deadline: .now() + .seconds(3)) [weak self] in
guard let self = self else return
self.queue.cancelAllOperations()
print("Cancel tasks")
class SimpleOperation: Operation
private let title: String
private var counter: Int
private let delayInUsec: useconds_t
init(title: String, counter: Int, delayInUsec: useconds_t)
self.title = title
self.counter = counter
self.delayInUsec = delayInUsec
override func main()
if isCancelled return
while counter > 0
print("\(title), counter: \(counter)")
counter -= 1
usleep(delayInUsec)
if isCancelled return
方式2.1 DispatchWorkItemController
DispatchWorkItem DispatchWorkItem cancel()解决方案
protocol DispatchWorkItemControllerDelegate: class
func workСompleted(delegatedFrom controller: DispatchWorkItemController)
class DispatchWorkItemController
weak var delegate: DispatchWorkItemControllerDelegate?
private(set) var workItem: DispatchWorkItem?
private var semaphore = DispatchSemaphore(value: 1)
var needToStop: Bool
get
semaphore.wait(); defer semaphore.signal()
return workItem?.isCancelled ?? true
init (block: @escaping (_ needToStop: ()->Bool) -> Void)
let workItem = DispatchWorkItem [weak self] in
block return self?.needToStop ?? true
self.workItem = workItem
workItem.notify(queue: DispatchQueue.global(qos: .utility)) [weak self] in
guard let self = self else return
self.semaphore.wait(); defer self.semaphore.signal()
self.workItem = nil
self.delegate?.workСompleted(delegatedFrom: self)
func setNeedsStop() workItem?.cancel()
func setNeedsStopAndWait() setNeedsStop(); workItem?.wait()
基础溶液的使用(完整样本)
class ViewController: UIViewController
lazy var workItemController1 = self.createWorkItemController(title: "Task1", counter: 50, delayInUsec: 100_000) ()
lazy var workItemController2 = self.createWorkItemController(title: "Task2", counter: 10, delayInUsec: 500_000) ()
override func viewDidLoad()
super.viewDidLoad()
DispatchQueue.global(qos: .default).async(execute: workItemController1.workItem!)
DispatchQueue.global(qos: .default).async(execute: workItemController2.workItem!)
DispatchQueue .global(qos: .background)
.asyncAfter(deadline: .now() + .seconds(3)) [weak self] in
guard let self = self else return
self.workItemController1.setNeedsStop()
self.workItemController2.setNeedsStop()
print("tasks canceled")
private func createWorkItemController(title: String, counter: Int, delayInUsec: useconds_t) -> DispatchWorkItemController
let controller = DispatchWorkItemController needToStop in
var counter = counter
while counter > 0
print("\(title), counter: \(counter)")
counter -= 1
usleep(delayInUsec)
if needToStop() print("canceled"); return
controller.delegate = self
return controller
extension ViewController: DispatchWorkItemControllerDelegate
func workСompleted(delegatedFrom controller: DispatchWorkItemController)
print("-- work completed")
方式2.2队列控制器
在此处添加DispatchWorkItemController的代码
protocol QueueControllerDelegate: class
func tasksСompleted(delegatedFrom controller: QueueController)
class QueueController
weak var delegate: QueueControllerDelegate?
private var queue: DispatchQueue
private var workItemControllers = [DispatchWorkItemController]()
private var semaphore = DispatchSemaphore(value: 1)
var runningTasksCount: Int
semaphore.wait(); defer semaphore.signal()
return workItemControllers.filter $0.workItem != nil .count
func setNeedsStopTasks()
semaphore.wait(); defer semaphore.signal()
workItemControllers.forEach $0.setNeedsStop()
func setNeedsStopTasksAndWait()
semaphore.wait(); defer semaphore.signal()
workItemControllers.forEach $0.setNeedsStopAndWait()
init(queue: DispatchQueue) self.queue = queue
func async(block: @escaping (_ needToStop: ()->Bool) -> Void)
queue.async(execute: initWorkItem(block: block))
private func initWorkItem(block: @escaping (_ needToStop: ()->Bool) -> Void) -> DispatchWorkItem
semaphore.wait(); defer semaphore.signal()
workItemControllers = workItemControllers.filter $0.workItem != nil
let workItemController = DispatchWorkItemController(block: block)
workItemController.delegate = self
workItemControllers.append(workItemController)
return workItemController.workItem!
extension QueueController: DispatchWorkItemControllerDelegate
func workСompleted(delegatedFrom controller: DispatchWorkItemController)
semaphore.wait(); defer semaphore.signal()
if let index = self.workItemControllers.firstIndex (where: $0.workItem === controller.workItem )
workItemControllers.remove(at: index)
if workItemControllers.isEmpty delegate?.tasksСompleted(delegatedFrom: self)
QueueController 的使用(完整示例)
class ViewController: UIViewController
let queue = QueueController(queue: DispatchQueue(label: "queue", qos: .utility,
attributes: [.concurrent],
autoreleaseFrequency: .workItem,
target: nil))
override func viewDidLoad()
super.viewDidLoad()
queue.delegate = self
runTestLoop(title: "Task1", counter: 50, delayInUsec: 100_000)
runTestLoop(title: "Task2", counter: 10, delayInUsec: 500_000)
DispatchQueue .global(qos: .background)
.asyncAfter(deadline: .now() + .seconds(3)) [weak self] in
guard let self = self else return
print("Running tasks count: \(self.queue.runningTasksCount)")
self.queue.setNeedsStopTasksAndWait()
print("Running tasks count: \(self.queue.runningTasksCount)")
private func runTestLoop(title: String, counter: Int, delayInUsec: useconds_t)
queue.async needToStop in
var counter = counter
while counter > 0
print("\(title), counter: \(counter)")
counter -= 1
usleep(delayInUsec)
if needToStop() print("-- \(title) canceled"); return
extension ViewController: QueueControllerDelegate
func tasksСompleted(delegatedFrom controller: QueueController)
print("-- all tasks completed")
【讨论】:
【参考方案5】:我不确定您是否可以停止当前正在执行的块,但您可以调用 dispatch_suspend 来阻止队列执行任何新的队列项。然后您可以调用 dispatch_resume 来重新开始执行(但听起来这不是您想要做的)。
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
【讨论】:
【参考方案6】:请参阅 NSOperationQueue 上的 cancelAllOperations。您仍然需要确保您的操作正确处理取消消息。
【讨论】:
【参考方案7】:在尝试解决我自己的类似问题时,我找到了解决此类问题的有趣方法。基本概念是,无论什么类调用调度,它都有一个 id 属性来跟踪某些方法的当前执行,对我来说,它正在打开一个警报视图。调用 dispatch 的方法然后保存一个生成的 id 的局部变量。如果 id 没有改变,那么我知道不要取消我的回调。如果已更改,则不采取任何措施,因为其他警报已控制:
class AlertData: ObservableObject
static var shared = AlertData()
@Published var alertOpen = false
@Published var alertMessage = ""
@Published var alertTitle = ""
var id: UUID = UUID()
func openAlert()
// ID is used to only dismiss the most recent version of alert within timeout.
let myID = UUID()
self.id = myID
withAnimation
self.alertOpen = true
DispatchQueue.main.asyncAfter(deadline: (.now() + 2), execute:
// Only dismiss if another alert has not appeared and taken control
if self.id == myID
withAnimation
self.alertOpen = false
)
func closeAlert()
withAnimation
self.alertOpen = false
【讨论】:
【参考方案8】:另一种解决方案是丢弃旧队列并创建一个新队列。这个对我有用。这就像删除一个数组,您可以删除其中的每个元素,也可以简单地创建一个新的来替换旧的。
【讨论】:
但是你是怎么做到的?? 我不知道你是怎么做到的,但我认为这行不通。您必须释放队列,但队列上的每个块都保留对它的引用,因此在所有块完成之前实际上不会从内存中清除它。 tl;dr 这将导致大量内存泄漏。【参考方案9】:今天早些时候正在解决一个类似的问题,如果用户在完成之前导航离开,我想放弃为视图控制器加载数据所涉及的任务。基本上,我最终确定的方法是在 DispatchQueue
执行的闭包中使用对控制器的弱引用,并编写代码以在它消失时优雅地失败。
【讨论】:
以上是关于如何停止调度队列中任务的执行?的主要内容,如果未能解决你的问题,请参考以下文章