bfiber_1000
Boost.Fiber when_any, produce first outcome, whether result or exception
when_any, produce first outcome, whether result or exception
결과 또는 예외 여부에 관계없이 첫 번째 결과를 생성
우리는 어떤 작업 기능에서도 예외가 발생하지 않을 것이라고 보장할 수 없는 환경에서 실행되고 있지 않을 수 있습니다.
이 경우 위의 wait_first_something() 구현은 순진(naïve)합니다.
파이버 관리(Fiber Management) 섹션에서 언급했듯이 작업 파이버 중 하나에서 포착되지 않은 예외로 인해 std::terminate()가 호출됩니다.
적어도 그러한 예외가 첫 번째 결과를 기다리는 파이버에 전파되는지 확인합시다.
future<>를 사용하여 반환 값이나 예외를 전송할 수 있습니다.
따라서 단순히 T 대신 future< T > 항목을 보유하도록 wait_first_value()의 buffered_channel<>을 변경합니다.
future<>가 있으면 값을 반환하거나 예외를 다시 발생시키는 future::get()을 호출하기만 하면 됩니다.
지금까지는 훌륭했지만 타이밍 문제가 있습니다.
큐의 buffered_channel::push()에 대한 future<>를 어떻게 얻어야 할까요?
fibers::async()를 호출할 수 있습니다. 이는 확실히 작업 함수에 대한 future<>를 생성할 것입니다.
문제는 너무 빨리 돌아올 것이라는 것입니다! 우리는 queue<>에서 완료된 작업에 대한 future<> 항목만 원합니다.
실제로 우리는 먼저 완료되는 future<>만을 원합니다.
wait_first_outcome()에 의해 시작된 각 파이버가 async() 호출 결과를 push()하는 경우 대기열은 가장 빨리 완료되는 항목이 아닌 가장 왼쪽 작업 항목의 결과만 보고합니다.
async()가 반환한 future에 future::get()을 호출하는 것은 옳지 않습니다.
future<> 인스턴스당 get()을 한 번만 호출할 수 있습니다!
그리고 예외가 있는 경우 소비자 측으로 전파되는 대신 대기열의 생산자 측에 있는 도우미 파이버 내부에서 다시 발생합니다.
future::wait()를 호출할 수 있습니다.
이는 future<>가 준비될 때까지 도우미 파이버를 차단하며, 그 시점에서 wait_first_outcome()에 의해 검색되도록 push()할 수 있습니다.
그것은 효과가 있을 것입니다. 하지만 추가 파이버 생성을 피하는 더 간단한 전술이 있습니다.
packaged_task<>에 작업 함수를 래핑할 수 있습니다.
자연스럽게 packaged_task<>를 새 파이버에 전달하는 것을 생각하는 반면(실제로는 async()가 수행하는 작업),
이 경우 대기열의 생산자 끝에 있는 도우미 파이버에서 이미 실행 중입니다!
간단히 packaged_task<>를 호출하면 됩니다.
해당 호출에서 반환되면 작업 함수가 완료되었습니다. 즉, packaged_task<>에서 얻은 future<>가 확실히 준비되었음을 의미합니다.
그 시점에서 우리는 그것을 큐에 간단히 push()할 수 있습니다.
호출하는 방법은 다음과 같습니다.