본문 바로가기

iOS

[Concurrency] 동시성 프로그래밍

728x90

동시성 프로그래밍 관련 개념

 

쓰레드

주로 PC 사양에서 많이 접하는 용어로

4코어 8쓰레드 / 8코더 16쓰레드 .. 등으로 접했을 것 

 

쓰레드란?

간단하게 말해서 컴퓨터의 일을 처리하는 부분

 

🤔 8쓰레드, 16쓰레드라면 일을 처리하는 부분이 한개가 아님에도 버벅거리는 이유가 무엇인가?

예를 들어서 4코어라면 최소한 8개의 쓰레드가 존재
즉, 컴퓨터의 일하는 부분이 최소 8개 = 여러 개가 존재한다.

그럼에도 불구하고 한 개의 쓰레드만 일을 하도록 시켰기 때문에 
일을 할 수 있는 부분이 여러 개 있음에도 불구하고 한 개의 쓰레드만 일을 하고 있고,
하필 또 그 쓰레드가 제일 일이 어려운(빡센?) 화면을 관리하고 있기 때문이다.
🤔 그 전에는 안 그랬는데 왜 특정 일을 할 때만 버벅거리는가?

그 전에는 그만큼(버벅일만큼) 큰 일을 시키지 않았기 때문

예를 들어서 이미지를 여러 셀에 보여주는 경우
1. 네트워크를 통해 압축 파일을 다운로드.
2. 압축 파일 해제
3. 이미지 파일 변형
4. 셀에 이미지 표시 
... 그리고 이런 셀이 여러개 존재 
-> 그래서 부하가 걸리게 된다. (컴퓨터가 현재 소화할 수 있는 그 이상의 작업을 시켰기 때문)

 

여러 개의 쓰레드가 있음에도 불구하고 한 개의 쓰레드만 사용하고 있는 것은

복사기가 여러 개 있음에도 불구하고 한 대의 복사기에서만 출력을 하고 있는 것과 같은 상황이다.

 

 

동시성 프로그램의 목적

여기서 알 수 있는 동시성 프로그래밍의 목적은 아래와 같다.

어떻게 일을 여러 쓰레드에 분산시킬 수 있을까?

(= 작업을 어떻게 분산시킬 수 있을까?)

(= Task를 어떻게 다른 쓰레드로 보내 동시에 일을 할 수 있을까?)

 

 

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

iOS에서는 Task를 대기 행렬에 보내기만 하면 알아서 OS가 다른 쓰레드로 분산 

*대기 행렬 = 큐를 의미 

*큐 : 먼저 들어온 순서대로 배치 (FIFO - 주의할 점은 먼저 들어오고, 그래서 먼저 배치되지만 그렇다고 해서 먼저 작업이 끝나는 것은 아니다.)

 

 

🟢 iOS 개발자가 고려할 점 

Task를 Queue에 보내자 !! 

보내는 코드를 배우자 !!

 


GCD / Operation

iOS 프로그래밍에서 큐(대기행렬)라고 표현하는 것의 종류는 크게 두 가지가 있다.

 

  1. GCD (= Dispatch Queue)
  2. Operation

✔️ 직접적으로 쓰레드를 관리하지 않고, 큐를 이용해서 작업을 분산 처리한다.

✔️ GCD/Operation을 사용해서 시스템에서 알아서 쓰레드 숫자를 관리한다. 

 

💭 직접 쓰레드를 생성하지 않는 이유?

하드웨어 또는 일의 부하와 같은 시스템 지식 없이 직접 쓰레드를 생성할 경우, 오히려 앱이 더 느려질 수 있다.

 

✔️ GCD/Operation은 쓰레드보다 높은 레벨/차원에서 일을 한다고 볼 수 있다.

✔️ 오래 걸리는 작업들이 다른 쓰레드에서 비동기적으로 동작할 수 있도록 한다. (보통 iOS에서 오래 걸리는 작업은 네트워크 통신과 관련된 부분이다.)

 

 

Queue

어떻게 큐로 보낼까?

 

DispatchQueue.global().async {
    // 보낼 작업물
}

 

위 코드의 의미는 아래와 같다.

  • Dispatch : '보내다'
  • global() : 글로벌 큐에
  • async : 비동기적으로

= "작업물을 글로벌 큐에 비동기적으로 보낼거야"라는 의미

= 여기서 말하는 작업물은 클로저 안의 하나의 구문을 말한다. 

 

func secondTask0() -> String {
    return "★ 작업1, "
}

func secondTask1(inString: String) -> String {
    return inString + "작업2, "
}

func secondTask2(inString: String) -> String {
    return inString + "작업3 ★"
}


// 순서가 필요한 작업 실행해보기
DispatchQueue.global().async {
    let out0 = secondTask0()
    let out1 = secondTask1(inString: out0)
    let out2 = secondTask2(inString: out1)
    print(out2)
}

위의 코드를 실행하면 작업이 순서대로 진행 된다.

클로저 안의 하나의 블럭이 하나의 작업을 의미한다.

그리고 이러한 작업을 큐로 보내면(-> 위의 코드에서는 global 큐를 의미) iOS가 알아서 작업을 다른 쓰레드로 분산한다. 

 

 

GCD

  • 디스패치 큐
  • 주로 클로저로 묶일 수 있는 간단한 일에 사용
  • 함수를 사용하는 작업

 

Operation

GCD를 기반으로 만들어졌으며 GCD에 기능이 추가된 것이다.

(취소, 순서 지정, 일시 정지와 같은 기능이 추가되었다.)

 

  • 오퍼레이션 큐
  • 복잡한 일
  • 데이터와 기능을 캡슐화한 객체

 

GCD와 Operation 중에서 무엇을 써야 하는가?
정답은 없지만,
프로젝트의 효율성 및 적합성에 따라서 다른 기능을 적용해야 한다.

 


Async/Sync

비동기(Async)

작업을 보내고 작업의 완료와 상관 없이 바로 리턴 (기다리지 않는다.)

 

보내기만 하고 일이 끝나는 것에 상관 없이 다른 일을 한다.

= 일을 시작시키고 작업이 끝날 때까지 안기다린다.

= 메인 쓰레드가 다른 일을 할 수 있다.

 

DispatchQueue.global().async {
    
}

 

1️⃣ 원래의 작업이 진행되고 있던 곳(메인 쓰레드)에서 디스패치 글로벌 큐로 작업을 보내고,

2️⃣ 클로저 안의 코드인 작업을 기다리지 않는다. 

 

 

동기(Sync)

작업을 보내고 끝날 때까지 기다린다. 

 

작업을 보내고 다음 작업을 시작하지 않는다.

작업을 시작시키고 해당 작업이 끝날 때까지 기다린다.

 

DispatchQueue.global().sync {
    
}

1️⃣ 원래의 작업이 진행되고 있던 곳(메인 쓰레드)에서

2️⃣ 디스패치 글로벌 큐로 보낸 작업을 

3️⃣ 끝날 때까지 (동기적으로) 기다린다.

 

 

🤔 동기적으로 일을 처리한다면, 쓰레드로 보내는 것이 의미가 있는가?

어차피 같은 쓰레드라면, 작업이 끝날 때까지 기다려야 하므로 의미가 있다고 할 수 없다.
그래서 실질적으로 동기처리를 하게 된다면, 다른 쓰레드로 보내지 않는다. 

대부분의 코드는 async를 사용 (응용적으로 sync를 다룬다.)

 

 

🔴 결론 🔴

비동기 : 작업을 다른 쓰레드에서 하도록 한 후 그 작업이 끝나길 안기다리고 다음 일을 진행한다.
(안기다려도 다음 작업을 생성할 수 있다.)

동기 : 작업을 다른 쓰레드에서 하도록 한 후 그 작업이 끝나길 기다렸다가 다음 일을 진행한다. 
(기다렸다가 다음 작업을 생성한다.)

 

 

💭 비동기 개념이 필요한 이유?

  • 대부분 서버와의 통신 때문
  • 네트워크와 관련된 작업들은 내부적으로 비동기적으로 구현되어 있다. 

 


Serial/Concurrency

보내고 받는 방식에 대해서 비동기/동기로 나뉘어진다면,

어떤 속성의 큐로 보낼 것인가에 대해서 직력/동시로 나눌 수 있다.

 

 

Serial Queue

큐에 들어온 작업을 단 하나의 다른 쓰레드로 보내는 작업 

 

 

Concurrent Queue

큐에 들어온 작업을 여러 쓰레드로 보내는 작업 

(= 몇 개의 쓰레드로 분산할지는 시스템이 알아서 결정, 쓰레드가 여러 개로 분산된다는 것.)

 

 

🔴 결론 🔴

직렬 큐 : 다른 한 개의 쓰레드에서 
(보통 메인에서 분산시킨 작업을 다른 한 개의 쓰레드에서 처리하는 큐)

동시 큐 : 다른 여러 개의 쓰레드에서
(보통 메인에서 분산시킨 작업을 다른 여러 개의 쓰레드에서 처리하는 큐)

 

 

🤔 동시 큐가 무조건 좋아보이는데, 왜 직렬 큐가 필요할까?

✔️ 직렬 큐는 순서가 중요한 작업을 처리할 때 
✔️ 동시 큐는 독립적이지만 유사한 여러 개의 작업을 처리할 때 사용할 수 있다. (그래서 대표적인 예시로 테이블 뷰를 생각할 수 있는데, 각자 독립적인 셀이지만 유사한 작업을 하기 때문이다.)

 

 


 

그렇다면?!!?

비동기라는 말과 동시라는 말이 같은 말인가? ❌

 

비동기는 작업을 보내는 시점에서 결과를 기다릴지 말지에 대한 것

동시는 대기열로 보낸 작업을 여러 개의 쓰레드로 보낼지 아니면 하나의 쓰레드로 갈지에 대한 것

 

 

'iOS' 카테고리의 다른 글

Custom Navigation Back Gesture  (0) 2022.09.10
[Concurrency] GCD  (0) 2022.09.06
[Memo] 메모 앱을 만들어보자. (절정)  (0) 2022.09.06
[Memo] 메모 앱을 만들어보자. (위기)  (2) 2022.09.06
[Memo] 메모 앱을 만들어보자. (전개)  (0) 2022.09.06