[2026] C++ stop_token | 중단 토큰 가이드

[2026] C++ stop_token | 중단 토큰 가이드

이 글의 핵심

C++20 std::stop_token·stop_source·stop_callback과 std::jthread로 협력적 취소를 구현하는 방법입니다. 워커 루프, condition_variable_any 연동, 콜백 수명 주의점을 실전 코드와 함께 설명합니다.

stop_token이란?

std::stop_token 은 C++20에서 도입된 스레드 중단 요청 메커니즘입니다. 스레드에게 중단을 요청하고, 스레드는 주기적으로 중단 요청을 확인하여 안전하게 종료할 수 있습니다. std::jthread와 함께 사용하면 자동으로 통합됩니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <thread>
// 실행 예제
void worker(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        // 작업
    }
}
std::jthread t(worker);
t.request_stop();  // 중단 요청

왜 필요한가?:

  • 협력적 중단: 스레드가 안전하게 종료할 수 있도록 협력
  • 타입 안전: 전역 플래그 대신 타입 안전한 메커니즘
  • 콜백 지원: 중단 시 자동으로 콜백 실행
  • 표준화: 일관된 중단 패턴 다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ 전역 플래그: 타입 불안전, 경쟁 조건
std::atomic<bool> g_stop = false;
void worker() {
    while (!g_stop) {
        // 작업
    }
}
// ✅ stop_token: 타입 안전, 표준화
void worker(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        // 작업
    }
}

stop_token 구성 요소:

  • std::stop_source: 중단 요청을 발행하는 객체
  • std::stop_token: 중단 요청을 확인하는 객체 (복사 가능)
  • std::stop_callback: 중단 시 실행될 콜백 등록 아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
std::stop_source source;
std::stop_token token = source.get_token();
// 중단 요청
source.request_stop();
// 중단 확인
if (token.stop_requested()) {
    // 중단 처리
}

기본 사용

아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

void worker(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        std::cout << "작업 중..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    std::cout << "중단됨" << std::endl;
}
int main() {
    std::jthread t(worker);
    
    std::this_thread::sleep_for(std::chrono::seconds(1));
    t.request_stop();
}

실전 예시

예시 1: 조건 변수

다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

void worker(std::stop_token stoken) {
    std::mutex mtx;
    std::condition_variable_any cv;
    
    while (true) {
        std::unique_lock lock(mtx);
        
        if (cv.wait(lock, stoken, []{ return hasWork(); })) {
            processWork();
        }
        
        if (stoken.stop_requested()) {
            break;
        }
    }
}

예시 2: stop_source

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <stop_token>
std::stop_source ssource;
std::stop_token stoken = ssource.get_token();
void worker(std::stop_token st) {
    while (!st.stop_requested()) {
        // 작업
    }
}
int main() {
    std::thread t(worker, stoken);
    
    std::this_thread::sleep_for(std::chrono::seconds(1));
    ssource.request_stop();
    
    t.join();
}

예시 3: stop_callback

아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

void worker(std::stop_token stoken) {
    std::stop_callback callback(stoken,  {
        std::cout << "중단 콜백" << std::endl;
    });
    
    while (!stoken.stop_requested()) {
        // 작업
    }
}

예시 4: 여러 스레드

다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

int main() {
    std::stop_source ssource;
    std::vector<std::jthread> threads;
    
    for (int i = 0; i < 5; i++) {
        threads.emplace_back([stoken = ssource.get_token(), i]() {
            while (!stoken.stop_requested()) {
                std::cout << "스레드 " << i << std::endl;
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
            }
        });
    }
    
    std::this_thread::sleep_for(std::chrono::seconds(1));
    ssource.request_stop();  // 모든 스레드 중단
}

stop_callback

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

std::stop_token stoken;
std::stop_callback callback(stoken,  {
    std::cout << "중단 요청됨" << std::endl;
});
// request_stop 호출 시 콜백 실행

자주 발생하는 문제

문제 1: 체크 누락

아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 중단 체크 안함
void worker(std::stop_token stoken) {
    while (true) {
        // 중단 불가
    }
}
// ✅ 주기적 체크
void worker(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        // 작업
    }
}

문제 2: 블로킹 연산

다음은 cpp를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 블로킹 연산
void worker(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        blockingOperation();  // 중단 불가
    }
}
// ✅ 타임아웃
void worker(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        if (tryOperation(100ms)) {
            // 처리
        }
    }
}

문제 3: 콜백 수명

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

void worker(std::stop_token stoken) {
    int value = 10;
    
    // ❌ 댕글링 레퍼런스
    std::stop_callback callback(stoken, [&value]() {
        std::cout << value;  // 위험
    });
}
// ✅ 값 캡처
std::stop_callback callback(stoken, [value]() {
    std::cout << value;
});

문제 4: 여러 토큰

아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

std::stop_source source1, source2;
void worker(std::stop_token st1, std::stop_token st2) {
    while (!st1.stop_requested() && !st2.stop_requested()) {
        // 작업
    }
}

jthread와 통합

아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// jthread는 자동으로 stop_token 전달
std::jthread t( {
    while (!stoken.stop_requested()) {
        // 작업
    }
});
t.request_stop();
// 자동 조인

jthread vs thread + stop_token:

특징std::threadstd::jthread
자동 조인❌ 수동✅ 자동
stop_token❌ 수동 생성✅ 자동 전달
중단 요청❌ 수동 구현request_stop()
사용 편의성복잡간단
실무 비교:
다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ std::thread: 복잡
std::stop_source source;
std::stop_token token = source.get_token();
std::thread t([token]() {
    while (!token.stop_requested()) {
        // 작업
    }
});
source.request_stop();
t.join();  // 수동 조인
// ✅ std::jthread: 간단
std::jthread t( {
    while (!stoken.stop_requested()) {
        // 작업
    }
});
t.request_stop();
// 소멸자에서 자동 조인

jthread 동작 원리: 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// jthread의 개념적 구현
class jthread {
    std::stop_source ssource_;
    std::thread thread_;
    
public:
    template<typename F>
    jthread(F&& f) {
        thread_ = std::thread(std::forward<F>(f), ssource_.get_token());
    }
    
    void request_stop() {
        ssource_.request_stop();
    }
    
    ~jthread() {
        if (thread_.joinable()) {
            request_stop();
            thread_.join();
        }
    }
};

실무 패턴

패턴 1: 워커 스레드 풀

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class WorkerPool {
    std::vector<std::jthread> workers_;
    
public:
    WorkerPool(size_t numWorkers) {
        for (size_t i = 0; i < numWorkers; ++i) {
            workers_.emplace_back([i](std::stop_token stoken) {
                while (!stoken.stop_requested()) {
                    if (auto task = getTask()) {
                        processTask(*task);
                    } else {
                        std::this_thread::sleep_for(std::chrono::milliseconds(10));
                    }
                }
                std::cout << "워커 " << i << " 종료\n";
            });
        }
    }
    
    // 소멸자에서 자동으로 모든 워커 중단
};

패턴 2: 타임아웃과 중단

다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

template<typename F>
bool runWithTimeout(F&& f, std::chrono::milliseconds timeout) {
    std::stop_source source;
    std::jthread t([&f, stoken = source.get_token()]() {
        f(stoken);
    });
    
    std::this_thread::sleep_for(timeout);
    
    if (t.joinable()) {
        source.request_stop();
        return false;  // 타임아웃
    }
    
    return true;  // 완료
}
// 사용
bool success = runWithTimeout( {
    while (!stoken.stop_requested()) {
        // 작업
    }
}, std::chrono::seconds(5));

패턴 3: 리소스 정리

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class ResourceManager {
    std::jthread cleanupThread_;
    
public:
    ResourceManager() {
        cleanupThread_ = std::jthread([this](std::stop_token stoken) {
            std::stop_callback cleanup(stoken, [this]() {
                std::cout << "리소스 정리 중...\n";
                releaseResources();
            });
            
            while (!stoken.stop_requested()) {
                performMaintenance();
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
        });
    }
    
    void releaseResources() {
        // 리소스 정리
    }
    
    void performMaintenance() {
        // 유지보수 작업
    }
};

jthread와 stop_token의 관계 (C++20)

std::jthread는 스레드를 생성할 때 내부 std::stop_source를 하나 두고, 실행 함수가 다음 형태면 std::stop_token을 자동으로 넘깁니다.

  • void f(std::stop_token)
  • void f(std::stop_token, Args...) (추가 인자는 그 뒤) 호출 측에서는 request_stop() 으로 협력적 취소를 요청하고, jthread가 소멸될 때 조인을 시도합니다(구현은 자동 조인·중단 요청 정책을 따릅니다). 반면 std::threadstop_token과 무관이며, 직접 stop_source를 만들어 람다에 넘겨야 같은 패턴을 흉내 낼 수 있습니다.

stop_source, stop_token, stop_callback 역할 정리

타입역할
stop_source취소를 발행한다. request_stop() 호출 시 연결된 모든 토큰이 “중단됨”으로 바뀐다. 복사 시 같은 상태를 공유하는 소스가 늘어난다.
stop_token읽기 전용 뷰. stop_requested(), stop_possible() 등으로 확인한다. 가볍게 복사해 여러 스레드·알고리즘에 넘기기 좋다.
stop_callback토큰에 콜백을 등록한다. 등록 시점에 이미 request_stop()이 됐다면 즉시 콜백이 실행될 수 있다. RAII: 소멸 시 등록 해제(구현 정의 세부는 표준 참고).
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
std::stop_source src;
std::stop_token tok = src.get_token();
std::stop_callback cb(tok, [] { std::cout << "stop requested\n"; });
src.request_stop();  // 콜백 실행, 이후 tok.stop_requested() == true

협력적 취소 패턴 (왜 “강제 종료”가 아닌가)

표준 stop_token스레드를 즉시 죽이지 않습니다. 워커가 루프마다 stop_requested()를 확인하거나, 블로킹 연산을 중단 가능한 버전(타임아웃, condition_variable_any::waitstop_token 오버로드 등)으로 바꿔야 합니다. 권장 패턴:

  1. 짧은 단위 작업으로 나누고 매 단위 끝에 취소 확인.
  2. 대기wait_for / wait_until 또는 condition_variable_any + stop_token 조합.
  3. 정리가 필요하면 stop_callback으로 “락 해제·로그 flush” 등을 등록(콜백 안에서는 데드락 나기 쉬운 락을 조심).

실전 워커 스레드

일상 비유로 이해하기: 동시성은 주방에서 여러 요리를 동시에 하는 것과 비슷합니다. 한 명의 요리사(싱글 스레드)가 국을 끓이다가 불을 줄이고, 그 사이에 야채를 썰고, 다시 국을 확인하는 식이죠. 반면 병렬성은 요리사 여러 명(멀티 스레드)이 각자 다른 요리를 동시에 만드는 겁니다. 템플릿 아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// TaskQueue는 예시 이름입니다. pop_timeout이 없다면 아래 루프 패턴만 참고하세요.
// 실행 예제
void worker_loop(std::stop_token st, TaskQueue& q) {
    using namespace std::chrono_literals;
    while (!st.stop_requested()) {
        std::optional<Task> t = q.pop_timeout(50ms, st);
        if (!t) {
            if (st.stop_requested()) break;
            continue;
        }
        process(*t);
    }
    drain_remaining_work_optional(q);  // 정책에 따라
}

pop_timeout이 없다면 조건 변수 대기stop_token을 연동하거나, 짧은 sleepstop_requested()를 폴링하는 방식이 현실적인 타협입니다.

C++20에서 함께 쓰는 기능

  • std::jthread: 자동 stop_token 전달·조인 관례.
  • std::condition_variable_any: wait 계열에 stop_token을 받는 오버로드가 있어, 깨우기 없이도 취소에 반응하기 쉽습니다.
  • std::stop_callback: 취소 시 리소스 회수·메트릭 기록. 이들은 “프로세스 kill”이 아니라 애플리케이션 레벨 취소를 표준 형태로 맞추기 위한 도구입니다.

FAQ

Q1: stop_token은 무엇인가요?

A: C++20에서 도입된 스레드 중단 요청 메커니즘입니다. 스레드가 중단 요청을 확인하고 안전하게 종료할 수 있게 합니다.

Q2: 언제 사용해야 하나요?

A:

  • 스레드를 안전하게 중단해야 할 때
  • 취소 가능한 비동기 작업
  • 워커 스레드 풀 구현

Q3: jthread와 어떤 관계인가요?

A: std::jthread는 자동으로 stop_token을 생성하고 전달합니다. 람다의 첫 번째 인자가 stop_token이면 자동으로 전달됩니다.

Q4: 성능 영향은?

A: 매우 가볍고 빠릅니다. stop_requested() 호출은 원자적 플래그 확인만 수행합니다.

Q5: stop_callback은 언제 실행되나요?

A: request_stop() 호출 시 즉시 실행됩니다. 이미 중단 요청이 있었다면 콜백 등록 시 즉시 실행됩니다.

std::stop_source source;
source.request_stop();
std::stop_callback cb(source.get_token(),  {
    std::cout << "즉시 실행\n";
});
// 출력: "즉시 실행"

Q6: 여러 스레드가 같은 stop_token을 공유할 수 있나요?

A: 가능합니다. stop_token은 복사 가능하며, 모든 복사본이 같은 중단 상태를 공유합니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

std::stop_source source;
auto token = source.get_token();
std::jthread t1([token]() { /* ....*/ });
std::jthread t2([token]() { /* ....*/ });
source.request_stop();  // 모든 스레드에 중단 요청

Q7: stop_token 학습 리소스는?

A:

  • “C++20 The Complete Guide” by Nicolai Josuttis
  • “C++ Concurrency in Action” (2nd Edition) by Anthony Williams
  • cppreference.com - std::stop_token 관련 글: std::jthread, std::thread, Condition Variable. 한 줄 요약: stop_token은 스레드에게 중단을 요청하고 안전하게 종료할 수 있게 하는 C++20 메커니즘입니다.

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3