콘텐츠로 이동

Thread와 비동기 작업

작업의 내용과 그 작업을 언제 실행할지는 따로 표현할 수 있습니다. Runnable은 반환값 없는 작업을, Callable<T>는 결과를 반환하는 작업을 선언하는 인터페이스입니다. 작업·실행 주체·완료 신호를 구분해 읽고, 각 소스의 빌드·실행 버튼에서 현재 서버의 실제 결과를 확인할 수 있습니다. 선언된 API와 실행 지원 범위는 현재 실행 확인 범위에서 구분합니다.

implements Runnable을 사용하는 클래스는 public void run()을 구현합니다. 아래의 SumJob은 1부터 5까지 더하고 결과 필드에 저장합니다. 클래스를 정의하거나 Thread 객체를 생성하는 것만으로 작업을 시작했다고 판단할 수는 없습니다.

Thread("sum-worker", job)에 이름과 작업을 전달하고 start()로 시작하는 API 구조입니다. 결과를 읽는 곳 앞에는 join()을 둡니다.

import unibase.concurrent.Runnable;
import unibase.concurrent.Thread;

class SumJob implements Runnable {
    public int result;

    public void run() {
        int total = 0;
        for (int i = 1; i <= 5; i = i + 1) {
            total = total + i;
        }
        this.result = total;
    }
}

class ThreadJoin {
    static void main() {
        SumJob job = SumJob();
        Thread worker = Thread("sum-worker", job);
        worker.start();
        worker.join();
        print(job.result);
    }
}

다른 언어의 탭은 직접 작성한 대응 코드이며 컴파일러의 자동 생성 결과가 아닙니다. 언어마다 API와 실행 환경이 다릅니다. 이 페이지의 빌드·실행은 Unibase 탭에서 사용할 수 있습니다.

서버 실행을 준비 중입니다.
출력 비교 기준
15
Unibase 소스 내려받기

검증 상태: 문법 검사 통과 · 실행 출력 일치 (2026-10-06)

읽기 기준 결과와 실제 실행 출력 비교

코드를 읽어 정한 비교 기준입니다. 실행 성공 결과로 대신 표시하지 않습니다.

15

실제 네이티브 실행: 빌드 passed, 실행 passed, 종료 코드 0

15
현재 실행 확인 범위

읽기 순서는 작업 생성 → Thread 생성 → 시작 → 완료 대기 → 결과 사용입니다. 현재 native 런타임은 운영체제 스레드를 생성하고 완료를 기다립니다. join()으로 완료한 뒤에는 작업이 기록한 합계 15를 읽을 수 있습니다. join() 없이 결과를 읽으면 작업이 아직 완료되지 않았을 수 있고, 여러 작업이 같은 필드를 동시에 변경하는 코드는 별도의 동기화가 필요합니다.

Thread API 선언에서 확인한 형태
start() 작업 시작 요청
join() 완료 대기
join(int timeoutMilliseconds) 시간 제한을 받으며 bool 반환
interrupt() 인터럽트 요청
isAlive(), name() 상태와 이름 조회
Thread.current(), Thread.sleep(int) 현재 Thread 조회, 밀리초 단위 대기

현재 native 구현에서 join(timeoutMilliseconds)는 시간 안에 완료하면 true, 대기 시간이 지나면 false를 반환합니다. false는 작업을 종료했다는 뜻이 아닙니다. interrupt()와 Thread.current()는 Standard 선언에 있지만 현재 문서의 실행 서버에서는 지원하지 않습니다. 호출하면 실행 진단이 나오므로 강제 종료나 취소용으로 사용하지 마세요.

결과가 있는 작업은 Callable<int>의 call()로 표현합니다. Executor에는 작업자 수를 전달하고, submitInt()는 정수 결과를 받을 Future<int>를 반환하는 선언입니다.

import unibase.concurrent.Callable;
import unibase.concurrent.Executor;
import unibase.concurrent.Future;

class SquareJob implements Callable<int> {
    public int call() {
        return 5 * 5;
    }
}

class ExecutorFuture {
    static void main() {
        Executor executor = Executor(2);
        Future<int> future = executor.submitInt(SquareJob());
        int result = future.awaitResult();
        executor.shutdown();
        print(result);
    }
}

다른 언어의 탭은 직접 작성한 대응 코드이며 컴파일러의 자동 생성 결과가 아닙니다. 언어마다 API와 실행 환경이 다릅니다. 이 페이지의 빌드·실행은 Unibase 탭에서 사용할 수 있습니다.

서버 실행을 준비 중입니다.
출력 비교 기준
25
Unibase 소스 내려받기

검증 상태: 문법 검사 통과 · 실행 출력 일치 (2026-10-06)

읽기 기준 결과와 실제 실행 출력 비교

코드를 읽어 정한 비교 기준입니다. 실행 성공 결과로 대신 표시하지 않습니다.

25

실제 네이티브 실행: 빌드 passed, 실행 passed, 종료 코드 0

25
현재 실행 확인 범위

awaitResult()는 결과를 얻는 단계이고 shutdown()은 작업 제출을 마친 뒤 호출하는 종료 API입니다. 실제 계산 결과는 25입니다. Future<T>에는 get(), awaitResult(), cancel(), isDone(), isCancelled()가 선언되어 있습니다. 이 예제만으로 처리량·병렬 실행·종료 대기까지 검증한 것은 아닙니다.

Async.run()은 Runnable을 받고 완료를 나타내는 Task를 반환하는 선언입니다. await task;로 완료를 기다린 뒤 작업의 값을 사용하는 문법을 살펴봅니다.

import unibase.concurrent.Async;
import unibase.concurrent.Runnable;
import unibase.concurrent.Task;

class CountJob implements Runnable {
    public int count;

    public void run() {
        this.count = 3;
    }
}

class AsyncTask {
    static void main() {
        CountJob job = CountJob();
        Task task = Async.run(job);
        await task;
        print(job.count);
    }
}

다른 언어의 탭은 직접 작성한 대응 코드이며 컴파일러의 자동 생성 결과가 아닙니다. 언어마다 API와 실행 환경이 다릅니다. 이 페이지의 빌드·실행은 Unibase 탭에서 사용할 수 있습니다.

서버 실행을 준비 중입니다.
출력 비교 기준
3
Unibase 소스 내려받기

검증 상태: 문법 검사 통과 · 실행 출력 일치 (2026-10-06)

읽기 기준 결과와 실제 실행 출력 비교

코드를 읽어 정한 비교 기준입니다. 실행 성공 결과로 대신 표시하지 않습니다.

3

실제 네이티브 실행: 빌드 passed, 실행 passed, 종료 코드 0

3
현재 실행 확인 범위

Task는 완료를 기다리는 대상으로, 결과 값이 필요한 Future<T>와 역할이 다릅니다. task.await() 형태도 선언되어 있습니다. await 문법의 비차단 실행 방식이나 이벤트 루프 사용을 현재 런타임에서 확인하지 않았으므로 일반화하지 않습니다.

연결과 전송 API의 구조는 Net과 소켓 통신에서 이어집니다. 작업 대기를 생략하거나 Thread.sleep()으로 완료를 추측하는 방식 대신, 작업에 맞는 완료 신호를 확인하세요.

필요한 것 사용하는 선언 완료 또는 결과 확인
반환값 없는 작업 내용 Runnable.run() Thread·Task로 완료 확인
값이 있는 작업 내용 Callable<T>.call() Future<T>로 결과 확인
직접 생성한 실행 주체 Thread join()·상태 조회
작업자 수를 전달하는 실행 주체 Executor(int workers) 제출 결과의 Task·Future
완료를 나타내는 객체 Task await() 또는 await 문법
결과를 나타내는 객체 Future<T> get()·awaitResult() 또는 await 문법
결과·실패를 완료시키는 객체 Promise<T> future()로 조회 측에 전달

결과를 받는 호출자가 Future를 사용하는 동안 작업의 구현이 누구인지까지 알 필요는 없습니다. Promise는 그 Future를 완료하는 쪽의 API를 표현합니다. 이 관계를 메서드 반환 타입과 함께 읽으면 공유 필드를 직접 읽는 코드를 줄일 수 있습니다.

현재 선언에는 submit(Runnable): Task, submitString(Callable<string>): Future<string>, submitInt(Callable<int>): Future<int>가 있습니다. 임의의 T를 받는 submit<T>()가 있다고 가정하지 않습니다.

현재 native 런타임의 실행 순서는 실행 주체 생성, 작업 제출, 필요한 결과 대기, 종료입니다. shutdown()은 새 제출을 막고 이미 제출한 작업과 작업자 종료를 기다립니다. 종료 후 제출은 실행 오류입니다. isShutdown() 조회만으로 작업의 결과가 성공했다고 판단하지 말고 각 Future의 결과를 읽으세요. 작업자 수는 현재 실행 서버에서 1부터 64까지 받습니다.

isDone()은 완료 상태를, isCancelled()는 취소 상태를 조회합니다. 결과가 성공인지, 실패인지와 구분해서 다루세요. 현재 native 구현의 cancel()은 아직 시작하지 않은 작업만 취소할 수 있습니다. 실행 중이거나 이미 끝난 작업에는 false를 반환하며 스레드를 강제로 중단하지 않습니다. 취소된 결과를 읽으면 실행 오류가 나옵니다. 서버의 CPU·시간 제한에 따른 프로세스 종료는 이 API의 작업 취소와 다른 동작입니다.

Async.delay(int milliseconds)는 Task를 반환하는 선언입니다. Async.completedInt(int)와 Async.completedString(string)은 완료 값을 전달하는 Future 선언입니다. 완료된 값을 만드는 API는 실제 백그라운드 작업이 실행됐다는 증거가 아닙니다.

Promise<T>는 위 타입 관계를 이해하기 위한 선언 참고입니다. 현재 서버에서 직접 생성·완료·실패 처리는 지원하지 않습니다. 이 장의 실행 예제는 실제 지원하는 Thread·Executor·Async 경로를 사용합니다.

서로 다른 작업이 같은 값을 읽고 바꾸면 중간 순서에 따라 결과가 달라질 수 있습니다. Thread 예제는 계산 중인 값을 호출자가 동시에 바꾸지 않고 완료를 기다린 뒤 읽는 구조로 작성했습니다. 하지만 현재 선언에는 lock·mutex·atomic API나 메모리 모델의 상세 보장이 없으므로 임의의 동기화 API를 추가하지 않습니다.

시간이 오래 걸리는 작업에서는 결과와 함께 실패·취소·대기 종료도 고려해야 합니다. 오류와 예외에서 결과 대기 지점의 실패 처리 구조를 읽을 수 있습니다.

Thread의 합계가 15인지, Future의 계산 값이 25인지, Task 완료 뒤 값이 3인지 확인합니다. 작업 이름이나 started 문자열만 출력하는 검사는 계산·완료 순서·병렬 수행의 증거가 되지 않습니다. 여러 작업을 동시에 사용하는 예제는 각 작업의 결과와 완료를 확인한 뒤 확장해야 합니다.