タスクが完了するまで待機しています


99

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で作成しています。

回答:


228

DispatchGroupこれを実現するには、sを使用します。グループ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() {
    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()

    print(a) // you could also `return a` here
}

group.wait()現在のキュー(おそらくあなたのケースではメインキュー)をブロックdispatch.asyncするため、デッドロックを回避するために(上記のサンプルコードのように)別のキューを使用する必要があります。


別のクラスで関数を実行したいが、その関数が完了するのを待ってから、現在のクラスで処理を続行するにはどうすればよいですか?
Saeed Rahmatolahi、2018年

2
@SaeedRahmatolahi:waitアプローチを使用するか(ブロックしても問題ない場合、つまりメインスレッドにいない場合)、完了ハンドラーを提供するか、呼び出しクラスで通知アプローチを使用します。
浅いThought

なぜgroup.enter非同期ブロックの外で呼び出すのですか?グループに出入りするのは各ブロックの責任ではないでしょうか?
ビル

4
@Billはが終了するwaitまで待機しenterleave呼び出しのバランスをとります。enterクロージャーに入れた場合、まだ呼び出されていないwaitため待機enterせず、enterとのleave呼び出しの数バランスします(#enter == 0、#leav == 0)。
浅い

1
テスト用の@rustyMagnetこれはおそらく、進むべき道ではありません。XCTTestExpectation代わりにsを使用してください。このサンプルコードを
浅いThought '

26

Swift 3では、DispatchQueue1つのタスクを完了するときに完了ハンドラーは必要ありません。さらに、さまざまな方法で目標を達成できます

1つの方法はこれです。

    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を使用してタスクが完了したときにcompletionHandlerを使用する場合は、を使用できます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
}

1
私はそれらすべてを試しましたが、forループで

2

ディスパッチグループを使用する

   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.

5
これをメインキューで呼び出すと、デッドロックが発生します。
浅いThought

@shallowThoughtそうですね。
Prateekro 2017年

別のクラスで関数を実行したいが、その関数が完了するのを待ってから、現在のクラスで処理を続行するにはどうすればよいですか?
Saeed Rahmatolahi、2018年

1
上記の例はメインスレッドでブロックされますが、以前の回答が示したように、内部でラップしDispatchQueue.global().async{}てもメインキューはブロックされません。
JonnyB

2

ソリューションのSwift 5バージョン

func myCriticalFunction(){var value1:String?var value2:文字列?

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

}


1

スウィフト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!)
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.