Как использовать DispatchGroup в фоновом потоке? - PullRequest
0 голосов
/ 29 декабря 2018

У меня есть две функции (или задачи), которые я хочу запускать одну за другой, и я использую 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")
    }
}

Ответы [ 3 ]

0 голосов
/ 29 декабря 2018

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

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

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()

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

Прочтите это, чтобы лучше понять, что такое очередь: https://stackoverflow.com/a/53582047/9086770

0 голосов
/ 30 декабря 2018

Это решение представляет собой комбинацию первых двух ответов.Я использовал то, что @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 голосов
/ 29 декабря 2018

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

@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. требуемых ссылок. Каждый блок имеет ссылку на группу, поэтому он будет жить, пока все блоки не будут завершены.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...