如何在后台线程中使用DispatchGroup?

时间:2018-12-29 18:05:18

标签: swift

我有两个功能(或任务),我想一个接一个地运行,我正在使用DispatchGroup跟踪它们,并在完成时通知我。现在,它们已经在Main线程中完成了,但是我想在后台线程中运行这些任务。我将如何去做?我尝试了几种方法,但它们同时运行,或者在第一个方法完成后出现异常错误。下面的代码一个接一个地执行任务,但是如果我在函数内部调用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告诉我,即使在后台线程中调用它们,它们也在Main线程中执行。

 @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 :(得分:2)

鉴于您的描述,在这里我可能根本不会使用调度组。我只是链接方法:

@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 :(得分:0)

如果您要做的是在后台串行运行两个函数,那么您要做的就是依次在同一队列中的同一任务中执行它们。根本不需要花哨。

您可以将其插入操场并弄乱它:

require('dotenv').config({path: <path of .env file>});

无论是在串行队列中还是在并发队列中执行这两个功能,以及是以同步还是异步方式分派这两个功能,就将问题放在同一个队列中的同一任务而言,对于您而言都是微不足道的。但是,如果将这两个功能分成两个单独的任务(或队列),那么串行化和并发性就成为一个因素。但是,请注意,术语“并发”是相对的。通过调度队列(串行或并发)执行的所有任务都与主线程并发。但是,当我们在调度队列的上下文中谈论它们时,我们几乎总是意味着与其他任务并发。

请阅读以下内容,以更好地了解什么是确切的排队:https://stackoverflow.com/a/53582047/9086770

答案 2 :(得分: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()
}