2014년 6월 18일 수요일

Swift - NSThread 병렬 프로그래밍 기초

병렬 프로그래밍(Concurrency Programming)의 전통적인 강자라면 역시 스레드(Thread)가 있겠다. 이번에는 NSThread에 관한 소개글을 메모한다.

Objective-C 때도 그러했지만, 스위프트에서 NSThread는 상속받아 구현하는 형태보다는 별도의 메소드(셀렉터)를 스레드 함수로 등록하는 형태로 만들어서 동작시킨다. 이때 사용 할 수 있는 방법은 두 가지가 있다.
// 스레드를 만들어 바로 시작
NSThread.detachNewThreadSelector("runLoop", toTarget: self, withObject: nil)

// 스레드를 만들고 시작
let thread = NSThread(target: self, selector: "runLoop", object: nil)
thread.start()
위 두 가지 방법에서 모두 문자열로 “runLoop” 라는 셀렉터 이름을 넘겨주는데 이는 위 코드를 실행시키는 클래스에서 스레드로 돌릴 메소드 이름이다. 따라서 이 이름은 마음대로 지어서 설정해 주면 된다.

예제

실제로 스레드가 작동하는 코드 예제이다.
class MyThreadingClass: NSObject {
    func runLoop() {
        println("From MyThreadingClass")
    }
    
    init() {
        super.init()
        let thread = NSThread(target: self, selector: "runLoop", object: nil)
        thread.start()
    }
}
셀렉터를 사용해야 해서 어쩔 수 없이 NSObject를 상속받은 클래스를 만들었다. 대체로 실제 개발 시에는 대체로 NSObject를 상속받은 다른 클래스를 상속받아서 쓰는 경우가 많을테니 그다지 걱정 할 건 없을 것 같다. 다만 스위프트의 '최상의 클래스가 없다’는 특징이 좀 아쉬워진다.

어쨌든 이 클래스 인스턴스를 생성하면 init을 통해 스레드가 생성된다. 그리고 runLoop() 라는 메소드가 스레드에서 동작하게 된다.

위 예제는 루프가 없어서 콘솔에 로그 한줄 찍고 바로 스레드가 종료된다.

좀 더 현실적인 예제

스레드는 일반적으로 루프로 도는 것이 상식이다. 그리고 많은 자원을 점유하지 않기 위해 일정 시간 쉬면서 루프를 도는 것도 상식이다. 이런 상황을 전제로 위 예제를 고쳐봤다.
class MyThreadingClass: NSObject {
    func runLoop() {
        // 취소되기 전까지 무한루프
        while NSThread.currentThread().cancelled == false {
            println("From MyThreadingClass")
            
            // 1초간 휴식
            NSThread.sleepForTimeInterval(1)
        }
    }
    
    init() {
        super.init()
        let thread = NSThread(target: self, selector: "runLoop", object: nil)
        thread.start()
    }
}
스레드가 그냥 무한루프라면 문제가 많다. 그래서 스레드가 강제로 종료당하기 전까지만 무한으로 돈다는 가정이 필요하다. 그래서 while 문에서는 현재 스레드 인스턴스(NSThread.currentThread())에서 cancelled 프로퍼티 상태를 확인한다.

다만 위의 cancelled 프로퍼티는 OS X 10.10 요세미티부터 지원되는 방식이다. 10.9 이하 버전에서는 isCancelled 메소드를 활용해야한다.

NSThread의 sleepForTimeInterval() 이라는 메소드는 잠깐 쉬는 메소드이다. 즉 다른 스레드가 작업을 처리할 수 있게 잠깐 쉬어주는 용도다. C API인 sleep()은 쓰지 말고 NSThread.sleepForTimeInterval() 을 쓰자.

나머지 상세한 정보는 레퍼런스 문서를 참고하자.

[돌아가기] Swift - 병렬 프로그래밍(Concurrency Programming) 가이드
[관련글] 스위프트(Swift) 가이드

댓글 없음 :