작업이 완료 될 때까지 대기


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로 작성하고 있습니다.

답변:


229

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

2
@SaeedRahmatolahi : wait접근 방식을 사용하거나 (차단하는 데 문제가없는 경우, 즉 메인 스레드에 있지 않은 경우) 완료 핸들러를 제공하거나 호출 클래스에서 알림 접근 방식을 사용하십시오.
shallowThought

group.enter비동기 블록 외부에서 호출하는 이유는 무엇 입니까? 그룹에 들어오고 나가는 것은 각 블록의 책임이 아닌가?
Bill

4
@ 빌 wait때까지 대기 enterleave통화가 균형된다. enter클로저 를 넣으면 아직 호출되지 wait않았기 때문에 기다리지 enter않고 호출 수 enterleave호출 수가 균형을 이룹니다 (# enter == 0, # leav == 0).
shallowThought

1
테스트 용 @rustyMagnet 이것은 아마도 갈 길이 아닐 것입니다. XCTTestExpectation대신 s를 사용하십시오 . 참고 이 샘플 코드
shallowThought

26

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
}

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
메인 대기열에서 이것을 호출한다고 가정하면 교착 상태가 발생합니다.
shallowThought

@shallowThought 정말 사실입니다.
Prateekro

다른 클래스에서 함수를 실행하고 싶지만 해당 함수가 완료 될 때까지 기다렸다가 현재 클래스에서 계속 처리하려면 어떻게해야합니까?
Saeed Rahmatolahi

1
위의 예제는 이전 답변에서 볼 수 있듯이 기본 스레드에서 차단되지만 내부 래핑 DispatchQueue.global().async{}은 기본 대기열을 차단하지 않습니다.
JonnyB

2

솔루션의 Swift 5 버전

func myCriticalFunction () {var value1 : 문자열? 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

이러한 상황에 Async Function을 사용할 수 있습니다. 를 사용 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.