boost::asio::io_service::work 가 무엇인가 - 그리고 지금은 io_context와 executor_work_guard
예전에 만들어진 소스를 분석 중 boost::asio::io_service::work 가 나오길래 무엇인지 찾아보았다.
2026년 수정 안내 “작업이 없으면 강제 종료되는 것을 막기 위한 장치”라는 원래 설명이 부정확했다.
io_service는 강제 종료되지 않으며,run()이 조용히 반환될 뿐이다. 그리고io_service/work자체가 Boost 1.66(2017)부터 deprecated되어, 지금 새로 쓴다면io_context/executor_work_guard를 써야 한다. 둘 다 정리한다.
work가 막아주는 것 : “강제 종료”가 아니라 “run()의 조기 반환”
io_service::run()은 처리할 작업이 하나도 남지 않으면 즉시 반환된다. 대기 중인 비동기 작업(펜딩 핸들러)이 없으면 io_service는 “할 일이 없다”고 판단하고 run()을 빠져나온다.
문제는 서버처럼 당장은 할 일이 없어도 앞으로 들어올 작업을 계속 기다려야 하는 경우다. 예를 들어 아직 아무 연결도 안 들어온 시점에 run()을 호출하면, 등록된 핸들러가 없으므로 run()이 곧바로 반환되고 그 워커 스레드는 io_service를 처리할 수 없는 상태가 된다.
io_service::work 객체를 하나 만들어두면, io_service는 “아직 끝나지 않은 작업이 있다”고 판단해 run()이 반환되지 않고 계속 대기한다. work 객체가 소멸되어야 비로소 할 일이 없다는 판단으로 돌아간다.
boost::asio::io_service io_service;
boost::asio::io_service::work work( io_service ); // run()이 즉시 반환되지 않도록 붙잡아둔다
std::thread worker( [&io_service]() { io_service.run(); } );
// ... 나중에 들어오는 비동기 작업들이 io_service에 계속 등록되어 처리된다 ...
“강제 종료를 막는다”는 표현은 부정확하다. 프로세스나 io_service가 죽는 것이 아니라, run()이 할 일이 없다고 판단해 예상보다 일찍 반환되는 것을 막아주는 것이다.
Boost 1.66부터는 io_context / executor_work_guard
Boost 1.66(2017년 12월)에서 Asio가 크게 개편되면서 다음처럼 이름이 바뀌었다.
| 예전 (deprecated) | 현재 |
|---|---|
boost::asio::io_service |
boost::asio::io_context |
boost::asio::io_service::work |
boost::asio::executor_work_guard (make_work_guard로 생성) |
#include <boost/asio.hpp>
boost::asio::io_context io_context;
// work 대신 이렇게 만든다. RAII로 소멸자에서 자동 해제된다.
auto work_guard = boost::asio::make_work_guard( io_context );
std::thread worker( [&io_context]() { io_context.run(); } );
// ... 비동기 작업 등록 ...
work_guard.reset(); // 더 이상 일감이 없으면 여기서 명시적으로 놓아준다
executor_work_guard는 work와 역할이 같지만, reset()으로 명시적으로 놓아주거나 스코프를 벗어나면 자동으로 해제된다는 점이 더 명확하다.
표준 C++에 채택된 std::execution(C++26 예정)이나 여러 네트워킹 라이브러리도 Asio의 이 실행자(executor) 모델을 기반으로 하고 있어, 이 개념을 알아두면 다른 곳에서도 도움이 된다.
댓글 남기기