[CS193p] Multithreading

Lena·2020년 12월 7일
0

stanford-cs193p

목록 보기
1/1
post-custom-banner

Multithreading

멀티스레딩을 하는 이유는 긴 시간이 소요되는 작업을 메인 스레드 밖에서 하기 위해서이다.
앱의 UI는 항상 반응할 수 있는(responsive) 상태를 유지해야 하기 때문이다.

Queues

iOS는 멀티스레딩을 큐queue로 관리하는데 큐는 줄지어 선 긴 대기열을 말한다.
Queue에 functions(usually closures)이 들어있고, 순서대로 꺼내어 관련 스레드에서 실행된다.
Queue는 "serial"(one closure a time)과 "concurrent"(multiple threads servicing it)가 있다.
serial queue는 한 번에 하나의 클로저를 실행, concurrent queue는 여러 개의 스레드로 동시 실행된다.

Main queue

UI와 관련된 건 모두 메인 스레드 메인 큐에서 실행된다 = serial queue
따라서 UI를 방해하는 작업은 메인 큐 이외에 다른 큐에서 작업해야 한다.

Global Queues

main queue 외에서 작업할 때 주로 shared, global, concurrent queue인 global queue를 사용한다.

Getting a queue

  • main queue
// Getting the main queue
let mainQueue = DispatchQueue.main

UI activity는 반드시 메인 큐에서 실행되어야 한다.

  • global queue
    main queue가 아닌 background 작업들은 주로 global queue에서 한다.
// Getting a global, shared, concurrent "background" queue
let backgroundQueue = DispatchQueue.global(qos: DispatchQoS)

DispatchQoS.userInteractive // high priority, only do something short and quick
DispatchQoS.userInitiated // high priority, but might take a little bit of time
DispatchQoS.background // not directly initiated by user, so can run as slow as needed
DispatchQoS.utility // long-running background processes, low priority

Putting a block of code on queue

멀티스레딩은 큐에 클로저를 담는 과정이다. 크게 2가지 방법이 있다.

  • async
    current queue의 작업을 방해하지 않으면서 바로 클로저를 실행한다.
You can just plop a closure onto a queue and keep running on the current queue

queue.async { ... }
  • sync
    current queue의 실행 흐름을 끊고 클로저를 먼저 실행시킨다.
... or you can block the current queue waiting until the closure finishes on that other queue

queue.sync { ... }

Getting a non-global queue

main이나 global 이외의 큐가 필요한 경우도 드물게 있다.

// use this only if you have multiple, serially dependent activities
let serialQueue = DispatchQueue(label: "MySerialQ") // serial queue

// rare that you would do this versus global queues
let concurrentQueue = DispatchQueue(label: ""MyConcurrentQ", attributes: .concurrent)

Dispatch, known as GCD

멀티코어 환경에서 코드의 동시 실행을 위한 기술(프레임워크)이다. DispatchQueue는 GCD(Grand Central Dispatch)의 일부이다.

📌 docs
Dispatch Framework
Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.

OperationQueue and Operation

There are also another API to all of this.
일반적으로 DispatchQueue API를 사용하지만, 간결한 nesting of dispatching을 위해 OperationQueue and Operation이 사용되기도 한다.

Example of a multithreaded iOS API

  • URLSession
    main queue 밖에서 http URL로 데이터를 가져오는 API
let session = URLSession(configuration: .default)
if let url = URL(string: "http://stanford.edu/...") {
    let task = session.dataTask(with: url) { (data: Data?, response, error) in 
        // UI와 관련된 일을 하고 싶다면...
        DispatchQueue.main.async {
            // do UI stuff here
        }
    }
    task.resume()
}

Cassini demo

segue될 view controlle를 navi controller embed in
segue prepare 코드를 조금 수정해야 한다.

// 추가
var destination = segue.destination
if let navcon = destination as? UINavigationController {
    destination = navcon.visibleViewController ?? navcon
}

if let imageVC = destination as? ImageViewController {
    imageVC.imageURL = url //imageVC의 property(공개 API)
    imageVC.title = (sender as? UIButton)?.currentTitle // 버튼 text로 imageVC의 title 설정
}

extension을 활용할 수도 있다.

if let imageVC = segue.destination.contents as? ImageViewController {
    imageVC.imageURL = url //imageVC의 property(공개 API)
    imageVC.title = (sender as? UIButton)?.currentTitle // 버튼 text로 imageVC의 title 설정
}
                
extension UIViewController {
    var contents: UIViewController {
        if let navcon = self as? UINavigationController {
            return navcon.visibleViewController ?? self
        } else {
            return self
        }
    }
}
private func fetchImage() {
    if let url = imageURL {
        // global queue로 이미지를 가져오고
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            let urlContents = try? Data(contentsOf: url)
            // UI를 업데이트하는 일은 main queue에서
            DispatchQueue.main.async {
                if let imageData = urlContents, url == self?.imageURL { // image를 가져오는 동안 url이 업데이트될 수 있으므로 가장 최근의 url의 이미지만 가져오도록 체크 조건을 추가한다.
                    self?.image = UIImage(data: imageData)
                }
            }
        }
    }
}

Note

serial, concurrent:
queue의 종류로, 한 번에 하나의 클로저만 처리(serial)하거나 또는 여러 클로저를 동시에 실행(concurrent)한다.

async, sync:
현재 queue의 실행을 방해하느냐 아니냐의 차이, async는 현재 queue의 실행을 방해하지 않고 클로저를 실행시킨다. sync는 클로저가 실행되고 완료될 때까지 현재 queue의 작업을 중지한다.

post-custom-banner

0개의 댓글