동시성 프로그래밍 (Concurrency)

이진욱(JIN WOOK)·2024년 12월 24일
0

제비에관하여(Swift)

목록 보기
9/10

동시성 프로그래밍이라는건 작업을 나눠서 즉, 분산 처리를 어떻게 하는지에 대한 코딩 방법을 말한다.

예를 들어 iOS의 경우는 UI작업을 위한 메인 스레드 라는것이 존재한다.
서버에서 API를 통해 데이터를 받아와서 이 데이터를 앱에 띄운다고 가정해보자.
서버에 데이터를 요청하는 작업도 메인스레드 라는곳에서 진행하게 된다면 데이터를 요청하고 데이터를 받는데 까지 시간이 걸리기 때문에
뚜둑뚜둑 끊겨 보일것이다.
반면에 이 요청작업을 다른 스레드에 보내서 작업을 시켜 데이터를 어느공간에 저장시켜두고 메인스레드로 보낸다면 위와 같은 현상은 발생하지 않을것이다.

동시성 프로그래밍에서는 두가지 개념이 존재한다.

동기 (sync)

어떤 작업이 다른 스레드로 보내졌을때 이 작업이 끝날때 까지 다음 작업을 수행하지 못한다. 이 보내진 작업의 빈 공간은 Block 처리가 되어버린다.
간략하게 코드로 표현하면 이렇다.
syncFunc()는 동기작업을 진행하는 함수가 된다.

func syncFunc() {
    DispatchQueue.global().sync {
        print("동기작업")
    }
}
func syncTask() {
    print("작업시작")
    syncFunc()
    print("작업끝")
}

syncTask()를 실행 시키게 되면 결과는

  • 작업시작
  • 동기작업
  • 작업끝
    이렇게 순서대로 출력된다.

비동기 (async)

동기와는 반대로 다른 스레드로 보내진 작업의 종료와 상관없이 바로 다음작업을 수행 할수 있다.

func asyncFunc() {
    DispatchQueue.global().async {
        print("비동기 작업")
    }
}
func asyncTask() {
    print("작업 시작")
    asyncFunc()
    print("작업 끝")
}

동기 작업과 반대로 asyncTask()를 실행하게 되면
작업이 끝날때 까지 기다리지 않기 때문에

  • 작업시작
  • 작업끝
  • 비동기작업
    이렇게 출력됨을 확인할수 있다.

    여담으로 부트캠프 동시성 특강때 강사님께서 네트워크 연결이 끊겨서 강사님의 캠 화면이 멈춘적이 있었다.
    그때 당시에 웃기려고 채팅으로 비동기 처리중.. 이라고 채팅을 쳤었는데 이 개념에 대해서 복습을 하니까 당시에는 잘못알고 있었던걸 알게 되었다.
    그당시 상황은 비동기 처리보다는 동기 처리인 상황이 맞다.
    강사님이 네트워크 문제를 해결할 동안 수업 진행이(다른작업) 되지 않았었으니까.

iOS 에서의 동시성 프로그래밍

Task를 Queue에 보내기만 하면 iOS가 알아서 여러 스레드로 나눠서 분산처리를 한다. (즉, 직접 스레드를 관리하지 않아도 된다)
Queue는 FIFO(First In, First Out)으로 작동한다.
그렇기에 우리는 적절한 Queue에 보내기만 하면 된다.

Queue의 종류에는 DispatchQueue와 OperationQueue가 있는데 주로 사용하는게 DispatchQueue라고 해서 DispatchQueue에 대해서 공부하는 중이다.
OperationQueue는 나중에 여유가 생기면 공부해볼 생각이다.

직렬큐(Serial) 와 동시큐(Concurrent)

큐도 직렬큐와 동시큐로 나뉜다.

  • 직렬큐

직렬큐의 경우 큐에 보내게 되면 작업을 처리할 하나의 스레드만을 생성하고 순서대로 작업을 보내 처리하게 된다.
순서가 중료한 작업인 경우 직렬큐를 사용한다.

//직렬큐의 생성
let serialQueue = DispatchQueue(label: "serialQueue")

serialQueue.async {
    task1()
}
serialQueue.async {
    task2()
}

코드로 표현해보면 위와 같고, 결과는 task1(), task2()순서로 출력되게 된다.
async가 붙어서 비동기적으로 작업을 보내긴 하지만 단 하나의 스레드에 배치가 되어서 작업을 진행하기 때문에 순서대로 작업을 처리할수 밖에 없다.

  • 동시큐

    동시큐는 작업을 여러개의 스레드로 보내 작업 처리를 하게 된다.
    그러므로 출력 결과의 순서예상이 불가능하다!
    각 작업이 독립적이지만 유사한 작업인 경우 사용한다.

DispatchQueue(GCD-Grand Central Dispatch Queue)

이제 주로 사용하게 될 DispatchQueue의 종류에 대해서 알아보겠다.

  • DispatchQueue.main - 메인큐
    메인큐 (메인쓰레드 라고 봐도 무방하다)
    1번 스레드를 의미한다.
    단 하나만 존재하며 직렬큐의 특성을 가지고 있다.
  • DispatchQueue.global - 글로벌큐
    기본적으로 동시큐의 특성을 가진다.
    글로벌 큐는 여러 스레드로 작업을 보내는 동시큐의 특성을 가지고 있기 때문에 QOS (Qulity Of Service)라는 개념을 가진다.

    QOS란?

    큐의 서비스 품질을 말한다.
    이 품질이 높으면 높을수록 여러스레드를 사용한다.
    iOS가 알아서 중요순위를 인지하고, 스레드의 우선순위를 매긴다.
    우선순위를 매겨서 CPU가 해당 스레드에 집중하게 되면 성능을 최대치로 발휘 할수 있다.

QOS (Qulity Of Service)의 종류

DispatchQueue.global(qos: .userInteractive)
// userInteractive - 사용자 인터페이스
// 사용자와 직접 상호작용하는 작업에 권장된다.(UI반응 , 애니메이션 처리등)
DispatchQueue.global(qos: .userInitiated)
// userInitiated - 사용자 시작?
//사용자가 당장 필요하긴 하지만 비동기적으로 처리되는 작업에 사용한다.
 DispatchQueue.global()  
 // 디폴트 글로벌큐
DispatchQueue.global(qos: .utility)
DispatchQueue.global(qos: .background)
 DispatchQueue.global(qos: .unspecified)
  • Private Queue - 프라이빗, 커스텀 큐
    개발자가 직접 커스텀 해서 사용할수 있는 큐이다.

    프라이빗 큐를 생성하려고 하면 속성,타겟 등 여러가지로 커스텀해서 만들수 있는 큐이다.
    기본적으로 직렬큐의 특성을 가지지만,

    커스텀이 가능하기에 큐의 특성을 넣어주기만 하면 동시성큐의 생성도 가능하다.
    하지만 일반적으로 직렬큐를 사용하고 싶을때 사용한다고 한다.

다음은 GCD사용시 주의해야 할것에 대해서 공부해볼 예정이다.

profile
기술로부터 소외 되는 사람이 없도록 우리 모두를 위한 서비스를 만들고 싶습니다.

0개의 댓글

관련 채용 정보