본문 바로가기

iOS

[iOS] Thread란? 그리고 DispatchQueue란?

안녕하세요! 🤗🤗🤗

 

오늘은 Thread에 대해서 알아보려고 해요. 쓰레드라는 개념은 프로그램에서 비동기처리를 하기 위해서 만들어진 개념이에요. 이전엔 하나의 프로그램은 하나의 작업만 실행할 수 있었다면, 쓰레드 프로그래밍을 통해서 여러 작업을 동시에 수행 할 수 있게 되었다네요. 그럼 자세히 알아보러 가볼까요? ㅎㅎ

What is Thread?

쓰레드는 응용 프로그램 내부에서 여러 실행 경로를 구현하는 간단한 방법입니다.

라고 정의가 나와있습니다. 위의 뜻은 아래의 시나리오를 통해 이해할 수 있어요.

 

  1. 프로그램이 시작되면 main Thread를 통해 모든 동작이 구현됩니다.
  2. 이 때 동시성을 지원하는 애플리케이션은 하나의 Thread를 추가하여 main과 추가한 Thread 두가지를 동시에 작업할 수 있게 됩니다.

예를 들어, 데이터를 받아오는 코드가 있어요. main Thread만을 이용할 경우에 데이터를 받아오는데 시간이 오래 걸리면 UI는 멈춰있는 상태일 것이고, 사용자는 프로그램 동작이 멈췄다고 생각하고 강제종료할거지만. Thread를 추가하여 동시에 작업을 진행한다면, 데이터를 받아오면서 UI도 동작이 가능해요.

용어정리

  • Thread: 코드에 대한 별도의 실행 경로
  • Process: 여러 스레드를 포함할 수 있는 실행중인 실행 파일
  • Task: 수행해야하는 작업의 추상적 개념

Thread 사용 시 문제점이 있나요?

Thread는 동시에 작업을 처리하기 위해서 만들어진 개념인데 문제점이 있을까요? 네 있습니다!

  • Thread를 남발 할 경우에 문제가 생길 수도 있어요. 애플리케이션의 스레드는 동일한 메모리 공간을 공유하기 때문에 동일한 모든 데이터 구조에 접근할 수 있습니다. 두 스레드가 동일한 데이터 구조를 동시에 조작하려고 하면, 한가지 스레드의 변경사항을 무시하고 덮어 쓰여질 수가 있게 되고, 그 결과 데이터 구조가 손상 될 수 있어요!
  • 또한 코드에 불확실성을 추가해줘요. 이 말은 이해가 안되죠?! 조금 더 풀어쓰면 쓰레드를 잘못 이해하여 사용할 경우, 동기화와 타이밍을 맞추기 더 어려워진답니다. 결국, 이는 위의 미묘한 동작 변경부터 앱 충돌 및 위의 데이터 구조에 손상 현상까지 초래할 수 있어요.
  • 또한, 한가지 프로세스에서 여러개의 쓰레드를 동시에 작업할 경우, 메모리 소비와 CPU 시간 측면에서 별로라고 합니다.

그래서 아래의 대체 기술들이 있습니다.

  • 타이머
  • 별도의 프로세스
  • 비동기함수!!!
  • 유효시간알림

등등 Swift Programming Guide를 보시면 더 자세히 알아보실 수 있습니다.

 

비동기 디자인 접근

위와 같이 Thread를 직접 사용할 경우, Thread를 직접 생성해서 사용할 경우, 비동기 함수를 만들고 비동기함수가 동작할 thread를 생성한 후 그 함수를 실행시켜야 해요. 하지만 이러한 작업이 위험성도 높고, 귀찮은 작업이어서 그런지 apple에서 이런 동작을 쉽게 할 수 있도록 기능을 제공하고 있어요!

  • GCD(Grand Central Dispatch)
  • GCD는 쉽게 말해 개발자 대신해서 thread를 관리해줍니다. 비동기적으로 작동할 함수를 생성하면 이 함수를 실행시킬 thread를 GCD에 맞기고 개발자는 코드에만 집중할 수 있도록 해줍니다. 비동기적 작업에 필요한 thread 및 스케줄링을 GCD가 알아서 처리합니다.
  • OperationQueue.. 더 자세한 이야기는 나중에! 우린 DispatchQueue에 대해 알아봐야하니!

DispatchQueue

이제 여기서 DispatchQueue가 등장해요!!

DispatchQueue는 C언어 기반으로 되어있고, GCD의 일부분있으며. 이는 작업을 단일 또는 병행 실행을 가능하게 해줍니다!!

여기서 또 두가지개념을 설명할 수 있는데 아래와 같습니다.

  • serial : Queue 자체가 받아드린 직업을 한개의 쓰레드로만 보내는 큐 (miain)
  • Concurrent : 여러 쓰레드를 사용해서 작업을 처리하는 큐

아래는 dispatchqueue의 이점이라는데 그냥 적어보았어요.

  • 직접적이고 단순한 프로그래밍 인터페이스 제공
  • 자동적이고 포괄적인 thread 관리
  • 빠른 속도의 동작
  • 효과적인 메모리 관리

DispatchQueue 사용해보기

DispatchQueue에 대해서 써보고 개념들이나 몇가지 주의사항들을 보겠습니다~

Serail vs Concurrent

  • 먼저, 기본적으로 작업이 실행되는 순서는 아래와 같아요.
print("Task1 Done")
print("Task2 Done")
print("Task3 Done")
print("Task4 Done")
print("Task5 Done")

//Task1 Done
//Task2 Done
//Task3 Done
//Task4 Done
//Task5 Done
  • 일반적으로 생성한 코드들은 DispatchQueue.main.sync {}에서 돌아갑니다.
  • 여기서 몇가지를 다른 쓰레드를 통해 실행시켜볼게요.
let queue = DispatchQueue(label: "MyQueue")

// 1
queue.async {
    print("Task1 Done")
}
print("Task2 Done")
// 2
queue.sync {
    print("Task3 Done")
}
print("Task4 Done")
print("Task5 Done")

//Task2 Done
//Task1 Done
//Task3 Done
//Task4 Done
//Task5 Done
  • 여러번 실행시켜보면 순서가 위와 같이 나올때가 있을거예요. 위 코드를 번호대로 해석해볼게요.
    1. queue.async 내부의 작업은 비동기 처리되어 새로운 쓰레드에 배정받아 바로 외부의 Task2가 실행이 됩니다. 그리고 내부 동작이 완료가 되는 즉시 바로 출력해줍니다.
    2. queue.sync 내부의 작업은 serial큐로 작업되기 때문에 Task1보다 먼저 수행되지 않습니다
  • 이제 위 DispatchQueue를 conCurrent 속성을 주면 여러 쓰레드가 작동하게 되어, Task3이 Task1보다 먼저 수행될 수 있겠죠?

UI View는 main Thread에서만 사용

  • 사용자가 UI를 보면서 꾸준히 상호작용할 수 있도록 하는 것이 저의 목표입니다. 이에 맞게 작동 우선순위를 정해주어야합니다. Thread를 이용해서 뷰와 인터페이스 요소들은 기본 작동 대기열의 다른 항목들에 의해 차단되지 않도록 해야합니다!!!
let queue = DispatchQueue.init(label: "work-queue")
queue.async {
    self.doWork()
}

DispatchQueue.main.async {
    self.view.backgroundColor = .black
}

다양한 GCD 서비스 품질 (QoS) 유형 파악

  • iOS에서 작업을 수행하기 위한 몇가지 우선 순위가 있어요! 높은 품질에서 낮은 품질로 우선순위가 결정됩니다~ 서비스 품질로 자신만의 대기열을 만드는 것은 쉬워요! qos속성이 제공되기 때문입니다.
  • 순서는 애플문서 가서 확인할 수 있어요

현재 어떤 스레드인지 파악

  • Thread.current를 사용해서 파악할 수 있어요.
  • Thread.current.isMainThread를 사용해서 현재 메인쓰레드인지 확인할 수 있어요

DispatchQueue.main.sync 는 되도록 사용 X

Reference