Как я могу использовать DispatchGroup в фоновом потоке?

У меня есть две функции (или задачи), которые я хочу запускать одну за другой, и я использую DispatchGroup, чтобы отслеживать их и уведомлять меня, когда они завершены. Сейчас они выполняются в главном потоке, но я хочу запустить эти задачи в фоновом потоке. Как бы я поступил так? Я пробовал несколько подходов, но они либо запускаются одновременно, либо я получаю ошибку исключения после завершения первого. Следующий код выполняет задачи одну за другой, но если я вызываю Thread.current внутри функций, я вижу, что они выполняются в главном потоке.

@objc func doWorkFunctions(){
    taskGroup.enter()
    DispatchQueue.global(qos: .background).sync {
        self.firstFunction {
            self.taskGroup.leave()
        }
    }

    taskGroup.enter()
    DispatchQueue.global(qos: .background).sync {
        self.secondFunction {
            self.taskGroup.leave()
        }
    }

    taskGroup.notify(queue: .main) {
        print("All tasks completed")
    }
}

Если я использую следующий код, они запускаются одновременно, но в фоновом потоке.

@objc func doWorkFunctions(){
    taskGroup.enter()
    DispatchQueue.global(qos: .background).async {
        self.firstFunction {
            self.taskGroup.leave()
        }
    }

    taskGroup.enter()
    DispatchQueue.global(qos: .background).async {
        self.secondFunction {
            self.taskGroup.leave()
        }
    }

    taskGroup.notify(queue: .main) {
        print("All tasks completed")
    }
}

Я искал и искал, но я не могу найти ответ на мою проблему или ясность в этом отношении. Может ли кто-нибудь дать некоторые указания относительно того, что здесь происходит. Эти функции в данном случае. Они моделируют долгую задачу, чтобы попрактиковаться в отслеживании прогресса.

func firstFunction(completion: @escaping()->Void){
    print(Thread.current)
    if childProgressOne.isCancelled { return }
    for i in 1...5 {
        sleep(1)

        childProgressOne.completedUnitCount = Int64(i * 20)

        print("Child Progress One: \(childProgressOne.fractionCompleted)")
        print("Total Progress: \(totalProgress.fractionCompleted)")
    }
    completion()
}

func secondFunction(completion: @escaping()->Void){
    print(Thread.current)

    if childProgressTwo.isCancelled { return }
    for i in 1...5 {
        sleep(1)

        childProgressTwo.completedUnitCount = Int64(i * 20)

        print("Child Progress Two: \(childProgressTwo.fractionCompleted)")
        print("Total Progress: \(totalProgress.fractionCompleted)")
    }
    completion()
}

Это также выполняет их по порядку, но вызов Thread.current внутри функций говорит мне, что они выполняются в основном потоке, даже если их вызывают в фоновом потоке.

 @objc func doWorkFunctions(){
    DispatchQueue.global(qos: .background).sync {
        self.taskGroup.enter()
        self.firstFunction {
            self.taskGroup.leave()
        }
        self.taskGroup.enter()
        self.secondFunction {
            self.taskGroup.leave()
        }
    }

    taskGroup.notify(queue: .main) {
        print("All tasks completed")
    }
}
+1
источник поделиться
3 ответа

Учитывая то, что вы описали, я, вероятно, не использовал бы здесь группу рассылки. Я бы просто цепочка методов:

@objc func doWorkFunctions() {
    DispatchQueue.global(qos: .background).async {
        self.firstFunction {
            self.secondFunction {
                DispatchQueue.main.async {
                    print("All tasks completed")
                }
        }
    }
}

Но если у вас есть веская причина для группы, вам нужно использовать .notify для их синхронизации. .notify говорит: "когда группа пуста, отправьте этот блок в эту очередь".

@objc func doWorkFunctions(){
    let queue = DispatchQueue.global(qos: .background)

    taskGroup.enter()
    queue.async {
        self.firstFunction {
            self.taskGroup.leave()
        }
    }

    taskGroup.notify(queue: queue) {
        self.taskGroup.enter()

        self.secondFunction {
            self.taskGroup.leave()
        }

        self.taskGroup.notify(queue: .main) {
            print("All tasks completed")
        }
    }
}

(Вам, вероятно, не нужно, чтобы taskGroup был здесь свойством экземпляра. Вы могли бы сделать его локальной переменной и иметь меньше self. ссылок. Требуется ссылка. Каждый блок имеет ссылку на группу, поэтому он будет жить, пока все блоки не будут завершены. )

+1
источник

Если все, что вы хотите сделать, это запустить две функции последовательно в фоновом режиме, то все, что вам нужно сделать, это выполнить их по порядку в одной задаче в одной и той же очереди. Там нет никакой необходимости, чтобы стать модным на всех.

Вы можете подключить это к детской площадке и возиться с этим:

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

func firstFunction(completion: @escaping() -> Void) {
    for i in 1...5 {
        sleep(1)
        print(i, Thread.current)
    }
    completion()
}

func secondFunction(completion: @escaping() -> Void) {
    for i in 1...5 {
        sleep(1)
        print(i + 100, Thread.current)
    }
    completion()
}

func doWorkFunctions() {

    let serialQueue = DispatchQueue(label: "serial")
    //let concurrentQueue = DispatchQueue.global(qos: .default) <-- this will produce the same result

    serialQueue.async {
        firstFunction(completion: {
            print("first function done")
        })
        secondFunction(completion: {
            print("second function done")
        })
    }

}

doWorkFunctions()

Выполняете ли вы эти две функции в последовательной или параллельной очереди, и отправляете ли вы их в синхронизированном или асинхронном режиме, тривиально с точки зрения вашего вопроса, если вы поместите их в одну и ту же задачу в одной и той же очереди. Если вы, однако, разделили две функции на две отдельные задачи (или очереди, если на то пошло), то сериализация и параллелизм станут фактором. Однако, как предостережение, термин "параллельный" является относительным. Все задачи, выполняемые через очереди отправки (последовательные или параллельные), выполняются одновременно с основным потоком. Но когда мы говорим о них в контексте очередей отправки, мы почти всегда имеем в виду одновременное выполнение других задач.

Прочтите это, чтобы лучше понять, что такое очередь: fooobar.com/questions/24420/...

0
источник

Это решение представляет собой комбинацию первых двух ответов. Я использовал то, что @nard опубликовал во втором ответе, но также использовал DispatchGroup. Я понимаю, что в этом сценарии DispatchGroup на самом деле не нужен, но если бы это было так, это был бы способ сделать это. Спасибо @Rob Napier и @nard за руководство.

import UIKit
func workOne(completion: @escaping ()->Void){
    print(Thread.current)
    for i in 1...4{
        sleep(1)
        print(i)
    }
    completion()
}
func workTwo(completion: @escaping ()->Void){
    print(Thread.current)
    for i in 5...8{
        sleep(1)
        print(i)
    }
    completion()
}
func doWork(){
    let dispatchGroup = DispatchGroup()

    dispatchGroup.enter()
    workOne {
        dispatchGroup.leave()
    }
    dispatchGroup.enter()
    workTwo {
        dispatchGroup.leave()
    }
    dispatchGroup.notify(queue: .main) {
        print(Thread.current)
        print("completed!")
    }
}
DispatchQueue.global(qos: .default).async {
    doWork()
}
0
источник

Посмотрите другие вопросы по метке или Задайте вопрос