シリアルキューがある場合、メインスレッドから、すぐに実行を停止してすべてのタスクをキャンセルするように指示するにはどうすればよいですか?
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
これは非常に一般的な質問であり、以前に回答したことがあります。
簡単な答えは、GCDにはキャンセルAPIがないことです。自分でキャンセルコードを実装する必要があります。上記の私の答えでは、基本的にどのようにそれを行うことができるかを示しています。
Swift
を使用している場合、DispatchWorkItem
クラスにより、作業単位をキャンセルできます。個別に。
作業項目を使用すると、個々の作業単位のプロパティを直接構成できます。また、完了を待つ、完了について通知を受ける、および/またはそれらをキャンセルする目的で、個々のワークユニットに対処することもできます。 (iOS 8.0+ macOS 10.10+で使用可能)。
DispatchWorkItemは、実行可能な作業をカプセル化します。作業項目は、DispatchQueueおよびDispatchGroup内にディスパッチできます。 DispatchWorkItemは、DispatchSourceイベント、登録、またはキャンセルハンドラーとして設定することもできます。
↳ https://developer.Apple.com/reference/dispatch/dispatchworkitem
実行中の現在のブロックを停止できるかどうかはわかりませんが、dispatch_suspendを呼び出して、キューが新しいキューアイテムを実行しないようにすることができます。その後、dispatch_resumeを呼び出して実行を再開することができます(しかし、それはあなたがやりたいことではないようです)。
操作オブジェクトをキャンセルすると、オブジェクトはキューに残りますが、できるだけ早くタスクを停止する必要があることをオブジェクトに通知します。現在実行中の操作の場合、これは、操作オブジェクトの作業コードがキャンセル状態を確認し、実行中の操作を停止し、自分自身を終了としてマークする必要があることを意味します
ソリューション
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 }
}
}
}
ソリューション
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")
}
}
ここに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")
}
}
NSOperationQueueの cancelAllOperations を参照してください。操作がキャンセルメッセージを正しく処理することを確認するのは、まだあなた次第です。
別の解決策は、古いキューを破棄して新しいキューを作成することです。わたしにはできる。配列を削除するようなものです。配列上のすべての要素を削除するか、新しい配列を作成して古い配列を置き換えることができます。