ios 如何停止调度队列中任务的执行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6928752/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to stop the execution of tasks in a dispatch queue?
提问by Randall
If I have a serial queue, how can I, from the main thread, tell it to immediately stop execution and cancel all of its tasks?
如果我有一个串行队列,我如何从主线程告诉它立即停止执行并取消其所有任务?
回答by adib
There is no way to empty pending tasks from a dispatch queue without implementing non-trivial logic yourself as of iOS 9 / OS X 10.11.
从 iOS 9 / OS X 10.11 开始,如果不自己实现非平凡的逻辑,就无法从调度队列中清空待处理的任务。
If you have a need to cancel a dispatch queue, you might be better off using NSOperationQueue
which offers this and more. For example, here's how you "cancel" a queue:
如果您需要取消调度队列,最好使用NSOperationQueue
which 提供此等功能。例如,以下是“取消”队列的方法:
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
回答by l'L'l
If you're using Swift
the DispatchWorkItem
class allows works units to be cancelled individually.
如果您使用Swift
的DispatchWorkItem
类允许工作单位进行单独取消。
Work items allow you to configure properties of individual units of work directly. They also allow you to address individual work units for the purposes of waiting for their completion, getting notified about their completion, and/or canceling them. ( available for use in iOS 8.0+ macOS 10.10+ ).
工作项允许您直接配置单个工作单元的属性。它们还允许您处理单个工作单元,以便等待它们完成、收到它们完成的通知和/或取消它们。(可用于 iOS 8.0+ macOS 10.10+ )。
DispatchWorkItem encapsulates work that can be performed. A work item can be dispatched onto a DispatchQueue and within a DispatchGroup. A DispatchWorkItem can also be set as a DispatchSource event, registration, or cancel handler.
DispatchWorkItem 封装了可以执行的工作。可以将工作项分派到 DispatchQueue 和 DispatchGroup 中。DispatchWorkItem 也可以设置为 DispatchSource 事件、注册或取消处理程序。
? https://developer.apple.com/reference/dispatch/dispatchworkitem
? https://developer.apple.com/reference/dispatch/dispatchworkitem
回答by Ryan
This is a pretty common question, and one I've answered before:
这是一个很常见的问题,我之前回答过:
The short answer is that GCD doesn't have a cancellation API; you have to implement your cancellation code yourself. In my answer, above, I show basically how that can be done.
简短的回答是 GCD 没有取消 API;您必须自己实施取消代码。在我上面的回答中,我基本上展示了如何做到这一点。
回答by dtuckernet
I'm not sure if you can stop a current block that is executing, but you can call dispatch_suspend to prevent the queue from executing any new queue items. You can then call dispatch_resume to restart execution (but it doesn't sound like that is what you want to do).
我不确定您是否可以停止正在执行的当前块,但是您可以调用 dispatch_suspend 来阻止队列执行任何新的队列项。然后您可以调用 dispatch_resume 重新开始执行(但听起来这不是您想要做的)。
回答by Vasily Bodnarchuk
Details
细节
- Xcode Version 10.2 (10E125), Swift 5
- Xcode 版本 10.2 (10E125),Swift 5
Way 1. OperationQueue
方式一:OperationQueue
Canceling an operation object leaves the object in the queue but notifies the object that it should stop its task as quickly as possible. For currently executing operations, this means that the operation object's work code must check the cancellation state, stop what it is doing, and mark itself as finished
取消操作对象会将该对象留在队列中,但会通知该对象应尽快停止其任务。对于当前正在执行的操作,这意味着操作对象的工作代码必须检查取消状态,停止正在执行的操作,并将自身标记为已完成
Solution
解决方案
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 }
}
}
}
Way 2.1 DispatchWorkItemController
方式2.1 DispatchWorkItemController
Solution
解决方案
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() }
}
Usage of base solution (full sample)
基础溶液的使用(完整样品)
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")
}
}
Way 2.2 QueueController
方式2.2 QueueController
add code of DispatchWorkItemControllerhere
在此处添加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 { 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")
}
}
.workItem != nil } .count
}
func setNeedsStopTasks() {
semaphore.wait(); defer { semaphore.signal() }
workItemControllers.forEach { 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
}
}
}
.setNeedsStop() }
}
func setNeedsStopTasksAndWait() {
semaphore.wait(); defer { semaphore.signal() }
workItemControllers.forEach { ##代码##.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 { ##代码##.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: { ##代码##.workItem === controller.workItem }) {
workItemControllers.remove(at: index)
}
if workItemControllers.isEmpty { delegate?.tasksСompleted(delegatedFrom: self) }
}
}
Usage of QueueController (full sample)
QueueController 的使用(完整示例)
##代码##回答by Micah Hainline
See cancelAllOperationson NSOperationQueue. It's still up to you to make sure your operations handle the cancel message correctly.
请参阅NSOperationQueue 上的cancelAllOperations。确保您的操作正确处理取消消息仍然取决于您。
回答by Hyman
Another solution is to throw away the old queue and create a new one. It works for me. It's like to delete an array, you can delete every element on it or you can simply create a new one to replace the old one.
另一种解决方案是扔掉旧队列并创建一个新队列。这个对我有用。这就像删除一个数组,您可以删除其中的每个元素,也可以简单地创建一个新元素来替换旧元素。
回答by B Purtzer
I have found a fun solution to this type of problem when trying to solve my own similar issue. The basic concept is that whatever class calls the dispatch, it has a id property that tracks the current execution of some method, for me, it was opening an alert view. The method that calls the dispatch then holds a local variable of a generated id. If the id has not be changed, then I know not to cancel my callback. If it has been changed, then take no action because some other alert has taken control:
在尝试解决我自己的类似问题时,我找到了解决此类问题的有趣方法。基本概念是,无论哪个类调用调度,它都有一个 id 属性来跟踪某个方法的当前执行情况,对我来说,它正在打开一个警报视图。调用分派的方法然后保存生成的 id 的局部变量。如果 id 没有改变,那么我知道不要取消我的回调。如果它已更改,则不采取任何措施,因为其他一些警报已控制:
##代码##