Как в GCD отменить выполняющуюся задачу?

Ответ

GCD не предоставляет прямого механизма отмены уже запущенной задачи, но есть несколько обходных решений:

1. DispatchWorkItem (отмена до выполнения)

let workItem = DispatchWorkItem {
    print("Эта задача может быть отменена")
}

// Запланировать выполнение
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: workItem)

// Отменить до начала выполнения
workItem.cancel()

2. Флаг отмены для выполняющихся задач

class CancellableTask {
    private var isCancelled = false
    private let queue = DispatchQueue(label: "com.example.task", attributes: .concurrent)

    func start() {
        queue.async { [weak self] in
            guard let self = self else { return }

            for i in 1...100 {
                // Периодическая проверка флага
                if self.isCancelled {
                    print("Задача отменена на шаге (i)")
                    return
                }

                // Полезная работа
                Thread.sleep(forTimeInterval: 0.1)
            }
        }
    }

    func cancel() {
        queue.async(flags: .barrier) {
            self.isCancelled = true
        }
    }
}

3. OperationQueue (предпочтительный способ)

let operationQueue = OperationQueue()
let operation = BlockOperation {
    // Длительная операция
}

operationQueue.addOperation(operation)

// Отмена операции
operation.cancel()

Рекомендации:

  • Для сложных сценариев используйте OperationQueue с поддержкой зависимостей и отмены
  • В современных Swift проектах предпочитайте async/await с Task и Task.checkCancellation()
  • Всегда реализуйте периодическую проверку флагов отмены в длительных операциях

Ответ 18+ 🔞

А, слушай, вот это реально пиздец, как же бесит, когда тебе надо остановить какую-то хуйню, а она уже поехала! GCD, блядь, такая штука: запустил — и всё, поезд ушёл. Прямого рычага «СТОП, НАХУЙ!» там нет. Но русские, как всегда, нашли, где схитрить.

1. DispatchWorkItem (Отмена, пока не начал ебашить) Представь, ты поставил будильник, а потом передумал. Вот это оно.

let workItem = DispatchWorkItem {
    print("Эта задача может быть отменена")
}

// Запланировать выполнение
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: workItem)

// Отменить до начала выполнения
workItem.cancel()

Работает, только если задача ещё в очереди болтается, как сопля. Как только исполнитель схватил её — всё, пиши пропало, отменить не выйдет. Чистая лотерея, ёпта.

2. Самопальный флаг отмены (Для тех, кто уже в процессе) Это когда ты кричишь «СТОЙ!», а работяга периодически оглядывается, не ты ли это орёшь. Грязно, но работает.

class CancellableTask {
    private var isCancelled = false
    private let queue = DispatchQueue(label: "com.example.task", attributes: .concurrent)

    func start() {
        queue.async { [weak self] in
            guard let self = self else { return }

            for i in 1...100 {
                // Периодическая проверка флага
                if self.isCancelled {
                    print("Задача отменена на шаге (i)")
                    return
                }

                // Полезная работа
                Thread.sleep(forTimeInterval: 0.1)
            }
        }
    }

    func cancel() {
        queue.async(flags: .barrier) {
            self.isCancelled = true
        }
    }
}

Суть в том, что твоя длительная операция должна быть не упоротой и иногда спрашивать: «А не пора ли мне нахуй?». Если флаг isCancelled поднят — делаем ноги. Барьер тут, чтобы не было гонок, а то получишь пиздец, а не отмену.

3. OperationQueue (Царь-пушка, предпочтительный способ) Вот это уже серьёзно, блядь. Тут уже есть встроенная, нормальная отмена.

let operationQueue = OperationQueue()
let operation = BlockOperation {
    // Длительная операция
}

operationQueue.addOperation(operation)

// Отмена операции
operation.cancel()

Операция сама, внутри себя, должна уважительно проверять свойство isCancelled и вовремя сваливать. Если не проверяет — то это просто упырь несчастный, а не операция.

Рекомендации на полях:

  • Для сложной хуйни с зависимостями и очередями — только OperationQueue, без вариантов. Там и отмена, и приоритеты, и жди друг друга.
  • Если ты уже в современном Swift и используешь async/await, то там своя магия: Task и Task.checkCancellation(). Это уже цивилизация, а не наши костыли.
  • Запомни раз и нахуй: любая длительная операция ДОЛЖНА быть вежливой и периодически проверять, не послали ли её. Иначе это не операция, а говно вентилятором размазанное.