【问题标题】:Swift Threading with DispatchGroup使用 DispatchGroup 的 Swift 线程
【发布时间】:2019-02-05 22:29:26
【问题描述】:

我有以下代码,它使用DispatchGroup 在任务完成时得到通知:

func getSomething(completion: ()->()) {
    completion()
}

func doSomeWork(completion: ()->()) {
    let myGroup: DispatchGroup = DispatchGroup()
    for _ in 0..<10 {
        myGroup.enter()
        getSomething {
            myGroup.leave()
        }
    }
    myGroup.notify(queue: DispatchQueue.main) { // this here is the key
        completion()
    }
}

DispatchQueue.global().async {
    print("We are doing background work")
    doSomeWork {
        print("We are done with the work - but on main thread now!!!")
    }
}

所以我遇到的问题是我在线程上调用了某个函数,该函数有一个完成,该完成在某个固定队列上调用。

我的选择是:

  1. 检测 Thread.isMainThread 并通知主队列或后台队列
  2. 为所有函数调用传递我们正在处理的队列 问题:这不是一个真正的智能设计模式
  3. 处理这个完成有一个固定队列的事实,然后再次手动分派

但我对任何选项都不满意...我宁愿没有这么多调度。

想象一下:

DispatchQueue.global().async {
    print("We are doing background work")
    doSomeWork {
        DispatchQueue.global().async {
            print("Now we are in background again")
        }
    }
}

这已经是一个 3 级闭包,使用起来并不是很好,尤其是在后台异步调用中我们有另一个闭包或关卡时。​​p>

任何关于在这里做什么的帮助都会很棒!谢谢

【问题讨论】:

  • 我不清楚您要解决的问题。跨线程通信本质上是混乱的。
  • 我想在后台线程上运行所有内容,但有些函数在主线程上调用完成处理程序,同时在后台线程上调用。这会造成很多来回切换。
  • 闭包是在主线程中构造并关闭该环境的问题,还是其他原因?
  • 问题是我正在后台队列上调用函数 bla({ //block code })。 bla() 调用主线程上的完成处理程序,但由于调度组
  • @JanoschHübner 为什么不使用除 main 之外的其他队列来运行通知代码?

标签: swift grand-central-dispatch


【解决方案1】:

问题是我正在调用一个函数 bla({ //block code }) 后台队列。 bla() 调用 main 上的完成处理程序 线程虽然,由于调度组 - Janosch Hübner

再次检查你的 sn-p

func doSomeWork(completion: ()->()) {
    let myGroup: DispatchGroup = DispatchGroup()
    for _ in 0..<10 {
        myGroup.enter()
        getSomething {
            myGroup.leave()
        }
    }
    myGroup.notify(queue: DispatchQueue.main) { // this here is the key
        completion()
    }
}

你看,因为 getSomething 是同步的,你可以简单地写

func doSomeWork(completion: ()->()) {
    //let myGroup: DispatchGroup = DispatchGroup()
    for _ in 0..<10 {
        //myGroup.enter()
        getSomething {
            //myGroup.leave()
        }
    }
    //myGroup.notify(queue: DispatchQueue.main) { // this here is the key
        completion()
    //}
}

如果 getSomething 应该是异步的,请使用适当的 API 在某个组内运行它

func doSomeWork(completion: ()->()) {
    let myGroup: DispatchGroup = DispatchGroup()
    let queue = DispatchQueue.global()
    for _ in 0..<10 {
        //myGroup.enter()
        queue.async(group: myGroup) {
            getSomething {
            //myGroup.leave()
            }
        }
    }
    myGroup.notify(queue: DispatchQueue.main) { // this here is the key
        completion()
    }
}

doSomeWork(completion: ()-&gt;()) 在同一个线程上运行completion()(最好是在同一个队列上)很简单。

func doSomeWork(completion: ()->()) {
    let myGroup: DispatchGroup = DispatchGroup()
    let queue = DispatchQueue.global()
    for _ in 0..<10 {
        //myGroup.enter()
        queue.async(group: myGroup) {
            getSomething {
            //myGroup.leave()
            }
        }
    }
    //myGroup.notify(queue: DispatchQueue.main) { // this here is the key
        myGroup.wait()
        completion()
    //}
}

查看下一个 Playground 页面,了解 DispatchQueue.concurrentPerform 如何改变您的设计以及 goup 通知的工作原理

import PlaygroundSupport
import Dispatch

PlaygroundPage.current.needsIndefiniteExecution = true

let q0 = DispatchQueue.global()
let q1 = DispatchQueue(label: "my_queue", attributes: .concurrent)
let g = DispatchGroup()
let g1 = DispatchGroup()
q0.async(group: g) {
    print("1    message from \(q0): will do some concurrent jobs in the background")
    DispatchQueue.concurrentPerform(iterations: 5, execute: { (i) in
        sleep(1)
        print("\t",i)
    })
    print("2    message from \(q0): all concurrent jobs done")
    q0.async(group: g) {
        print("3    some other long time running on group...")
        sleep(3)
        print("3 ex")
    }
    q0.async(group: g1) {
        print("?    some other long time running on gifferent group...")
        sleep(4)
        print("? ex")
    }
    g1.notify(queue: .main, execute: {
        print("g1 empty")
    })
}
print("4    continue on main")
g.notify(queue: q1) {
    print("5    message from \(q1): finished a")
    DispatchQueue.main.async {
        sleep(1)
        print("6    from main, should stop playground execution?")
        //PlaygroundPage.current.finishExecution()
    }
    print("7    message from \(q1): finished b")

}
g1.notify(queue: .main) {
    print("8    from main, g1 is empty.")

}
print(" ... continue")

在我的环境中打印

1    message from <OS_dispatch_queue_global: com.apple.root.default-qos>: will do some concurrent jobs in the background
4    continue on main
 ... continue
8    from main, g1 is empty.
     0
     2
     1
     3
     4
2    message from <OS_dispatch_queue_global: com.apple.root.default-qos>: all concurrent jobs done
3    some other long time running on group...
?    some other long time running on gifferent group...
3 ex
5    message from <OS_dispatch_queue_concurrent: my_queue>: finished a
7    message from <OS_dispatch_queue_concurrent: my_queue>: finished b
6    from main, should stop playground execution?
? ex
g1 empty

【讨论】:

  • myGroup.wait() 会再次阻塞队列的工作,不是吗?
  • 它阻塞了执行 doSomeWork 的队列。你可以异步执行它......如果需要
  • @user3441734 在您的第 4 块中,您说我们可以只使用 queue.async(group: myGroup) { getSomething {} 但是这不起作用,如果 getSomething 里面是异步的并进入另一个队列,它不会处理组输入/正确离开并过早结束
  • @MarcoPappalardo 如果 getSomething {} 在内部是异步的,那么异步调用它的想法是什么?无论如何,在这种情况下,评论 myGroup.leave() 会立即调用,结果相同。
  • @user3441734 例如getSomething 是一个网络层。您希望并行启动许多网络请求并等待所有请求完成通知。 getSomething 的完成块仅在请求完成时执行。所以需要调用。我的意思是 group.leave() 是强制性的,它可以在你说“它是异步的”的例子中工作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多