인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

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

Presto님의 프로필 이미지
Presto

작성한 질문수

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

3) 블락오퍼레이션(BlockOperation)

BlockOperation을 Serial OperationQueue에 넣었을 때 Serial하게 실행되지 않는 것 같습니다.

작성

·

210

1

안녕하세요, 강의 잘 보고 있습니다.

 

BlockOperation 자체는 Operation에 등록된 block들이 concurrent하게 실행된다고 나와 있고, 이걸 serial하게 실행하고 싶으면 이 BlockOperation을 maxConcurrentOperationCount를 1로 설정한 OperationQueue에 넣으면 된다고 하신 걸로 이해했습니다.

그래서 다음과 같이 코드를 작성하고 돌려봤는데, 등록한 block의 순서대로 print되지 않는 걸 봐서는 serial하게 실행되지 않는 것 같습니다.

let operation = BlockOperation()
for i in 0 ..< 1000 {
  operation.addExecutionBlock {
    print(i, Thread.current)
  }
}

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
queue.addOperation(operation)

이 코드를 실행하면 i가 0부터 999까지 순서대로 프린트되지 않고 랜덤하게 프린트됩니다.

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1

for i in 0 ..< 1000 {
  queue.addOperation {
    print(i, Thread.current)
  }
}

반면에 위처럼 BlockOperation을 사용하지 않고 maxConcurrentOpreationCount가 1인 OperationQueue에 block을 순서대로 추가하면, 이전에 설명해주신 대로 serial하게 실행되어 0부터 999까지 차례대로 프린트됩니다.

Thread.current의 프린트 결과를 봐도 두 번째 코드보다 첫 번째 코드에서 더 많은 쓰레드를 사용하는 것으로 보입니다.

 

(질문과는 관계 없긴 한데, 이 과정에서 OperationQueue가 항상 동일한 쓰레드를 사용해서 Operation을 실행하지는 않는다는 것을 확인했습니다. 반면 serial하게 동작하는 커스텀 DispatchQueue는 항상 동일한 쓰레드를 사용하는 것으로 확인했습니다. maxConcurrentOperationCount가 1인 OperationQueue는 항상 동일한 쓰레드를 사용하지는 않더라구요. 사실 maxConcurrentOperationCount가 말 그대로 concurrent하게 실행될 수 있는 Operation의 개수를 말하는 것이지, 실행될 수 있는 쓰레드의 개수를 말하는 건 아니니까 이건 이것 나름대로 이해한 것 같습니다.)

 

executionBlocks를 얻어서 좀 더 봐보려고 했는데 잘 안되더라구요.

혹시 이 부분 관련해서 제가 잘못 이해한 부분이 있는지, 아니면 추가 설명을 부탁드릴 수 있을까요? 감사합니다.

답변 2

1

Presto님의 프로필 이미지
Presto
질문자

답변 감사합니다!

 

답변을 보고 BlockOperation의 문서를 봤더니 이미 문서에 BlockOperation은 block의 concurrent한 실행을 관리한다, block은 실행 환경에 대한 가정을 하지 않는다, 같은 내용이 있었네요. Operation은 기본적으로 현재 쓰레드에서 sync로 동작하는 반면, 이를 상속받은 BlockOperation의 block들은 concurrent하게 실행되는 것으로 이해했습니다.

쓰레드 관련해서도 이해했습니다. 말씀 주신 대로 개념적으로 이해하는 편이 좋은 것 같아요. 감사합니다!

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

네네 ^^ 화이팅..!!!

0

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

안녕하세요, Presto님.

블락 오퍼레이션에 내장되어 있는 블락들은 DispatchQueue.global( )을 사용하도록 되어 있어서,
쓰레드 1개만 사용하는 오퍼레이션큐로 보내도, 내부적으로 다시 비동기 처리가 되어서 무조건 동시적으로 동작하게 될꺼예요.

그래서 블락 오퍼레이션 자체가 원래 동시적처리 목적으로 사용하는 것이기 때문에,

사실 위에 작성하신 코드처럼 사용하시면 안되고 (오퍼레이션큐로 보낸다는 것은 단순 비동기 처리 목적이라고 이해하셔야 하고, 제가 강의에서 잘못 말씀드린 줄 알고 저도 다시 봤는데.. 그건 아니고.. 아마 제가 중간에 queue.maxConcurrentOperationCount = 1 이라는 코드를 넣어놓아서 오해하신 것 같긴 하네요.)


그래서.. 원하시는 대로 Serial로 동작하게 하시려면,
네, 아래 코드처럼 작성하시는 것이 맞습니다.
(Serial을 원하시면 Presto님께서 작성하신대로 아래코드 처럼 작성하거나,
오퍼레이션 하나씩 작성 ===> 쓰레드 1개 설정 ===> 오퍼레이션큐로 보내기 이런식으로 하셔야 하겠죠.)



그리고 괄호 내용에서 써주신 내용은..
질문 목록 아래쪽으로 조금만 내려가시면..
"Serial Queue가 한개의 쓰레드를 사용한다는 내용에 질문있습니다." 라는 질문을 한번 보시면 좋을 것 같습니다.

위의 질문을 보시면 거의 이해되실텐데, 
쓰레드라는 것도 사실 소프트웨어적으로 운영체제가 생성해낸 객체이기 때문에 1개의 쓰레드를 사용한다고 하더라도, 사실 잠깐 쉬는 동안에는 객체가 없어졌다가 다시 생길 수도 있어서.. 메모리 주소를 찍어봤을땐, 다른 객체일 수 있습니다.

즉, 제가 보통 강의에서 표현하는..  (예를 들어) "2번 쓰레드"라고 개념적으로 말씀드리는 내용은 (운영체제가 2번 쓰레드이라는 것으로 1개의 쓰레드로 앱에 할당하긴 하지만, 실제적으로 (내부에서 운영체제가 동작하는 메커니즘은) 실제 여러개의 객체가 번갈아서 동작할 수 있기 때문에.. 실제 메모리 주소를 찍어보면 1개이상도 될 수 있습니다.  (그래서, 메모리 주소를 직접적으로 찍어보는 것은.. 실제 거의 도움이 되지 않고..) 개념적으로 이해하시는 편이 낫다고 생각하긴합니다. (물론 1번 메인 쓰레드 같은 경우, 런루프도 동작하고, 쉴 일이 거의 없기 때문에 메모리 주소를 찍어보면 1개의 쓰레드 객체만 보이긴 하죠.)

혹시나 제가 위에 말씀드린 다른 질문 보시고, 이해가 안되시면 다시 말씀주세요.

 

감사합니다. :)

 

Presto님의 프로필 이미지
Presto

작성한 질문수

질문하기