• 카테고리

    질문 & 답변
  • 세부 분야

    모바일 앱 개발

  • 해결 여부

    해결됨

DispatchGroup 관련 질문

20.06.24 16:54 작성 조회수 158

1

안녕하세요 강사님 오랜만에 인사드립니다!

요즘 컨커런시 관련해서 예제코드도 작성해보고 나름 코드로 이리저리 놀던(?) 도중 신기한 상황이 발생되어서 질문드립니다.

SiwftUI로 작성되었으며 View부분은 아래와 같습니다.

import SwiftUI

struct ContentView: View {

    @ObservedObject var vm = ViewModel()

    

    var body: some View {

        VStack{

            Button(action: {

                self.vm.gcdTest.testFunc(boolean: true)

            }){

                Text("boolean = true")

            }

            

            Button(action: {

                self.vm.gcdTest.testFunc(boolean: false)

            }){

                Text("boolean = false")

            }

        }

        

    }

}

class ViewModel: ObservableObject{

    let gcdTest = GCDTest()

    init() {

        self.gcdTest.start()

    }

}

실제로 문제가 되는 부분은 아래와 같습니다.

import Foundation

let pGroup = DispatchGroup()

let pThread = DispatchQueue.init(label: "com.test", qos: .default)

class GCDTest{

    

    init() {

    }

    

    func start(){

        pGroup.enter()

        pThread.async {

            print("pThread async")

            pGroup.leave()

        }

    }

    func testFunc(boolean: Bool){

        if boolean{

            self.start()

            pGroup.notify(queue: pThread) {

                print("notify")

            }

        }else{

            print("else")

        }

    }

}

여기서 신기했던 점은 testFunc(boolean:) 메서드의 로직이 실행되지 않았을 때는 pGroup.leave() 가 호출이 되어도 notify()가 실행이 되지 않는다는 것입니다.

제가 상상하고 있던(예상하고 있던) 로직의 진행은 아래와 같은데 말이죠... 혹시 이와 관련해서 피드백이나 개념을 알려주실수있을까요?

답변 6

·

답변을 작성해보세요.

1

김기현님의 프로필

김기현

질문자

2020.06.25

늦은새벽에도 답글달아주셔서 감사합니다.

답변주신걸 보니 아차싶었네요... 아직도 많이 배워야 하는것 같습니다 ㅠ 

제가 잘못알고있는거 같습니다. notify가 leave 메서드가 호출된다면

1. 어떤 클로저 내에 있건 간에 호출이 되는 녀석이라고 생각을 했습니다.

같은 맥락으로 if 클로저 내부에서도 leave 를 만나면 실행이 된다고 생각을 했습니다.

예제 코드를 다시보니 1번이 된다고 착각을 했었네요.

1번이 되는 것이 아니라 testFunc()/true 내부에 start(), notify()가 있어서 notify가 다시 호출될 수 있었던거였네요 ㅠ

당연히 boolean이 false이때는 leave가 호출되어도 notify를 등록(?)하지 않았기에 호출이 안되는 것이였고요...

notify()라는 로직흐름만 따라가다보니 정작 중요한 것은 까먹고 있었습니다.

부끄럽네요... 다음에는 질문 올릴때 신중히 올리도록하겠습니다.

이런 질문에도 답변을 달아주시니 너무 감사할따름입니다 ㅠ 

좋은 밤되세요! 

1

김기현님의 프로필

김기현

질문자

2020.06.24

항상친절한 답변 감사드립니다! ㅎㅎ

말씀하신대로 url request를 이용해서 작성을 해봤고, request작업에는 delegate 패턴을 자주사용하니 그것도 함께 작성해봤습니다.

View부분은 그대로이고 문제가 되는 파일만 수정해서 올려드립니다.

import Foundation

let pGroup = DispatchGroup()

let pThread = DispatchQueue.init(label: "com.test", qos: .default)

class GCDTest{

    

    init() {

    }

    

    //task1

    func start(){

        pGroup.enter()

        Req(delegate: self).request()

    }

    //task4

    func testFunc(boolean: Bool){

        print("testFunc")

        if boolean{

            self.start()

            pGroup.notify(queue: pThread) {

                print("notify")

            }

        }else{

            print("else")

        }

    }

}

extension GCDTest: ReqDelegate{

    func response() {

        print("response")

        //task3

        pThread.async {

            print("pThread async")

            pGroup.leave()

        }

    }

    

    

}

protocol ReqDelegate {

    func response()

}

class Req{

    var delegate: ReqDelegate!

    init(delegate: ReqDelegate) {

        self.delegate = delegate

    }

    

    //task2

    func request() {

        // https://httpbin.org/#/Images/get_image

        let url: URL = URL(string: "https://httpbin.org/#/Images/get_image")!

        let request = URLRequest(url: url)

        let session = URLSession.shared

        session.dataTask(with: request) { (data, response, error) in

            if error != nil{

                return

            }else{

                self.delegate.response()

            }

        }.resume()

    }

}

실행 결과는 처음 시작시에는

boolean = true 번튼 클릭시

boolean = false 버튼 클릭시

이렇게 출력이 됩니다.

제가 예상했던 Flow는 다음과 같습니다.

현재 testFunc()메서드의 boolean 값에 따라서 notify가 실행되고 안되는 중입니다.

notify()가 leave() 가 호출되고 나면 반드시 실행되어지는 메서드라고 생각을 했는데 위와같은 코드를 보니 혼동이 있습니다.

notify()를 감싸고 있는 블럭들(testFunc(), if boolean)에 따라서 notify()가 실행되고 안되고가 결정되고 있는 상황인것같습니다.

이와같은 실행결과가 어떻게 나오는지가 궁금합니다. 감사합니다!

0

아닙니다..ㅎㅎ 가끔은 혼자 여러가지 생각을 하다보면 정작 어떤 포인트를 놓치게 되는 경우도  많죠^^ 그래도 꾸준히 정말 열심히 공부하시네요!!! 항상 좋은 질문들.. 생각할 거리들 남겨주셔서 감사합니다.

장마가 시작되었는데, 건강 유의하시고 좋은 하루 되세요 :)

0

기현님 죄송한데, 제가 기현님이 질문하시는 취지에 대해서 아직 제대로 파악이 안됩니다...

false를 전달했는데, 왜 notify( )메서드가 실행이 안되는가? 에 대해서 물으신게 맞으신 가요?

(====> 이 부분이 제가 의도를 제대로 파악한 것인지에 대해서 잘 모르겠습니다...)

만약에 혹시라도 질문하신 의도가 그것이 맞다면,

false를 전달 했으니 true내의 코드들은 컴퓨터가 읽을 수가 없는데 어찌 실행이 될 수 있을까... 라고 밖에 닶을 못해드릴 것 같은데..  기현 님이 올려주신 코드들을 그래도 플레이그라운드에 넣어서 구성해 보았는데..

혹시 이 코드를 보고 한번 생각해주시길 바랍니다.

https://drive.google.com/file/d/1fKdL7ZZRnPwsDePGzR1iqA8j5AvN8Eml/view?usp=sharing

0

조금 더 추가해서 말씀드리면,

만약 지금과 같은 프린트를 유지하려고 하신다면......

현재 원하시는 작업이 비동기적 그룹형태가 아니고

코드 내에 작업이 print("pThread async") 동기적 함수형태 이므로 

비동기 그룹 코드를 제거 하시고, (enter, leave를 제거하시고..)

pThread.async(group: pGroup) {

            print("pThread async")

}

위와 같이 동기적 작업 그룹 코드로 작업에 보내시면 될것 같습니다. ^^

0

안녕하세요 기현님! 일단, 하단의 그림이 깨져서 어떤 생각을 가지고 질문 하신 지는 정확하게 모르겠으나,

일단 해주신 질문 자체만으로 보면 notify메서드는 등록과 같은 개념입니다.

즉, 비동기 그룹에서 작업들이 시작되어있을때(enter) 등록을 해야만,

그 작업들이 다 끝났을때 알려주는 개념입니다.

그래서, 비동기 그룹 작업들에서 enter( ) 로 작업들이 일을 하고 있을때....

(예를 들자면 일을 하는 작업이 +1 이상일때), notify메서드가 등록이 되어야만, 합니다.

그런데 기현님이 쓰신 코드는

대부분 너무 짧은 작업이어서, 즉  print("pThread async") 이어서

이미 leave( ) 가 일어난 다음에

notify메서드가 등록이 되는 것으로 보입니다.

(즉 enter가 1도 없는데, 즉 이미 leave()가 다 실행되어서 enter가  0인 상태에서

notify가 등록이 되므로 실제 notify가  작업할 것이 없어보입니다.)

비동기 작업을 네트워크 작업과 같은 조금 긴 작업으로 바꿔보시면

될 것으로 보입니다.

혹시 문제가 있다면 다시 피드백 주세요 ^^ 감사합니다. :)