В ожидании завершения задачи
Как я могу заставить свой код ждать, пока задача в DispatchQueue завершится? Нужен ли какой-нибудь CompletionHandler или что-то?
func myFunction() {
var a: Int?
DispatchQueue.main.async {
var b: Int = 3
a = b
}
// wait until the task finishes, then print
print(a) // - this will contain nil, of course, because it
// will execute before the code above
}
Я использую Xcode 8.2 и пишу в Swift 3.
7 ответов
Использование DispatchGroup
Чтобы добиться этого. Вы можете получить уведомление, когда группа enter()
а также leave()
звонки сбалансированы:
func myFunction() {
var a: Int?
let group = DispatchGroup()
group.enter()
DispatchQueue.main.async {
a = 1
group.leave()
}
// does not wait. But the code in notify() gets run
// after enter() and leave() calls are balanced
group.notify(queue: .main) {
print(a)
}
}
или вы можете подождать (и вернуться):
func myFunction() -> Int? {
var a: Int?
let group = DispatchGroup()
group.enter()
// avoid deadlocks by not using .main queue here
DispatchQueue.global(attributes: .qosDefault).async {
a = 1
group.leave()
}
// wait ...
group.wait()
// ... and return as soon as "a" has a value
return a
}
Примечание: group.wait()
блокирует текущую очередь (вероятно, основную очередь в вашем случае), поэтому вы должны отправить dispatch.async в другую очередь (как в примере кода выше), чтобы избежать тупиковой ситуации.
В Swift 3 нет необходимости в обработчике завершения, когда DispatchQueue
заканчивает одну задачу Кроме того, вы можете достичь своей цели различными способами
Одним из способов является это.
var a: Int?
let queue = DispatchQueue(label: "com.app.queue")
queue.sync {
for i in 0..<10 {
print("Ⓜ️" , i)
a = i
}
}
print("After Queue \(a)")
Он будет ждать окончания цикла, но в этом случае ваш почтовый поток заблокируется.
Вы также можете сделать то же самое, как это
let myGroup = DispatchGroup()
myGroup.enter()
//// Do your task
myGroup.leave() //// When your task completes
myGroup.notify(queue: DispatchQueue.main) {
////// do your remaining work
}
Одна последняя вещь. Если вы хотите использовать завершение, когда ваша задача завершается с помощью DispatchQueue, вы можете использовать DispatchWorkItem
,
Вот пример, как использовать DispatchWorkItem
let workItem = DispatchWorkItem {
// Do something
}
let queue = DispatchQueue.global()
queue.async {
workItem.perform()
}
workItem.notify(queue: DispatchQueue.main) {
// Here you can notify you Main thread
}
Swift 5 версия решения
func myCriticalFunction() {var value1: String? var value2: String?
let group = DispatchGroup()
group.enter()
//async operation 1
DispatchQueue.global(qos: .default).async {
// Network calls or some other async task
value1 = //out of async task
group.leave()
}
group.enter()
//async operation 2
DispatchQueue.global(qos: .default).async {
// Network calls or some other async task
value2 = //out of async task
group.leave()
}
group.wait()
print("Value1 \(value1) , Value2 \(value2)")
}
Использовать группу рассылки
dispatchGroup.enter()
FirstOperation(completion: { _ in
dispatchGroup.leave()
})
dispatchGroup.enter()
SecondOperation(completion: { _ in
dispatchGroup.leave()
})
dispatchGroup.wait() //Waits here on this thread until the two operations complete executing.
В Swift 5.5+ вы можете воспользоваться Swift Concurrency, который позволяет возвращать значение из закрытия, отправленного в основной поток.
func myFunction() async {
var a : Int?
a = await MainActor.run {
let b = 3
return b
}
print(a)
}
Task {
await myFunction()
}
Swift 4
Вы можете использовать асинхронную функцию для этих ситуаций. Когда вы используете DispatchGroup()
Иногда возможны тупики.
var a: Int?
@objc func myFunction(completion:@escaping (Bool) -> () ) {
DispatchQueue.main.async {
let b: Int = 3
a = b
completion(true)
}
}
override func viewDidLoad() {
super.viewDidLoad()
myFunction { (status) in
if status {
print(self.a!)
}
}
}
Каким-то образом приведенные выше команды dispatchGroup enter() и leave() не работали в моем случае.
Однако мне помогло использование sleep(5) в цикле while в фоновом потоке. Ухожу отсюда на случай, если это поможет кому-то еще и не помешает другим моим темам.