인프런 커뮤니티 질문&답변

이한규님의 프로필 이미지
이한규

작성한 질문수

앨런 iOS Concurrency(동시성) - 디스패치큐와 오퍼레이션큐의 이해

serial 큐 질문드립니다.

작성

·

142

0

안녕하세요.

 

기본 구조는

A 스레드에서 SQ라는 serial queue에 async하게 task를 합니다.

전달한 task의 마지막 부분에 @escaping 하는 completionHandler()를 호출하는 구조입니다.

 

만약 A 스레드에서 SQ에 task1, task2 두개를 보내면 completionHandler가 호출되는 순서는 보장받을 수 없는거죠?

 

 

task1 {

serialqueue.async {

......

completionHandler()

}

}

 

task2 {

serialqueue.async {

......

completionHandler()

}

}

답변 1

0

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

한규님,
지금 현재 주신 질문내용을 제가 정확하게 이해하기 어려운 것 같아요!
질문을 다시 정확하게 코드 예시로 주시면 좋을 것 같습니다.

아래와 같은 예시라고 말씀하시는 것인지..
task1 / task2라는 이스케이핑 클로저에서 시리얼큐를 호출하신다는 것인지,
image

아니면 시리얼큐에서
task1 / task2를 호출하신 다는 것인지 잘 이해가 가지 않습니다.

image

 

지금 현재 올려주신 코드 자체에서는 오류가 납니다....

completionHandler라는 파라미터를 가진 인풋 함수 타입은 다른 함수 내에서 정의시에 호출하는 것이 일반적인데.. 어떤 의도를 가지고 말씀하시는지 전혀 이해가 되지 않아서,

엑스코드에서 코드를 직접 입력해보시고.. 캡처해서 질문을 남겨주시거나.. 정확한 코드를 다시 적어주시면 좋을 것 같습니다. :)

감사합니다...!

이한규님의 프로필 이미지
이한규
질문자

죄송해요 제가 좀 급하게 작성한 것 같습니다.

 

항상 아래의 결과(순서)를 보장받을 수 있는지 궁금합니다.

 시리얼큐 므로 task1, task2가 시리얼큐에 순서대로 올라가는건 맞는것같은데..

결과:

image

코드:

image

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

네, 일단 2가지 포인트가 있겠네요.


1) 첫번째 포인트는
시리얼큐이던, 동시큐이던 하나의 클로저에 작업을 보낸다는 것은.. 클로저에 묶여 있는 것이 "하나의 작업"이기 때문에.. 그건 당연히 순서대로 동작합니다.

DispatchQueue.global().async {
    print("task1_1")
    print("task1_2")
    print("task1_3")
    print("task1_4")
}

이렇게, (일부러 동시큐를 사용했지만) 하시면.. 당연히 출력은 순서대로 됩니다.

async { // 작업배치 } 부분에서 async 로 보내는 클로저에 있는 작업은 "하나의 작업"으로 보장받기 때문에.... 다른 말로 하자면, "클로저 내부는 당연이 순차적으로 동작"하기 때문에 위의 코드는 순서대로 동작합니다.

 

따라서, 한규님이 컴플리션 핸들러로 작성하신 코드에서 당연히 순서대로 동작합니다.

func task1(completionHandler: @escaping (Bool) -> Void) {
    serialQueue.async {
        print("task1_1")
        print("task1_2")
        completionHandler(true)
    }
}

위의 코드는.. 너무나 당연하겠지만 아래처럼 이렇게 쓰여있는 것과 똑같습니다.

func task1(completionHandler: @escaping (Bool) -> Void) {
    serialQueue.async {
        print("task1_1")
        print("task1_2")
        print("task1_3")
        print("task1_4")
    }
}



@escaping클로저라고 하더라도

print("task1_1")
print("task1_2")

작업을 한후에 콜백함수(컴플리션함수)를 호출하는 것은 순서대로 동작하기 때문에

completionHandler(true)


결국엔, 코드가 아래처럼 쓰여있는 것과 동일하고 당연히 순서대로 동작합니다.

print("task1_1")
print("task1_2")
print("task1_3")
print("task1_4")

그래서 아래처럼 쓰여있는 것과 별반 다를 것이 없다고 말씀드린 겁니다.
(그리고 당연하지만, 콜백함수를 호출하는 것은, 그냥 함수 내부의 일이 끝나고 다른 함수를 호출(실행)한 것뿐입니다.)

func task1(completionHandler: @escaping (Bool) -> Void) {
    serialQueue.async {
        print("task1_1")
        print("task1_2")
        print("task1_3")
        print("task1_4")
    }
}


2) 두번째 포인트는
시리얼큐간의 작업 배치 순서에 대한 질문인데..

let serialQueue = DispatchQueue(label: "serialQueue")

serialQueue.async {
    print("task1_1")
    print("task1_2")
    print("task1_3")
    print("task1_4")
}

serialQueue.async {
    print("task2_1")
    print("task2_2")
    print("task2_3")
    print("task2_4")
}

한규님이 작성하신 코드가 위와 완전히 같다고 보시면 되는데.. (함수의 호출 관계 제외하고.. 내부 작업의 로직만 보자면..)

결국엔 당연히 순서가 보장됩니다.
시리얼큐는 내부에 쓰레드가 1개인 순차 큐이고 순서대로 실행합니다. 따라서.. 사실 지금 하신 질문이.. 아래의 코드가 순서대로 동작하느냐? 라고 물어보신 것하고 완전히 동일합니다. 시리얼큐는 선입선출로 동작하니.. 일이 배치된 순서대로 무조건 동작을 하겠죠.

let serialQueue = DispatchQueue(label: "serialQueue")

serialQueue.async {
    print("task1_묶음")
}

serialQueue.async {
    print("task2_묶음")
}


감사합니다. :)

이한규님의 프로필 이미지
이한규

작성한 질문수

질문하기