読者です 読者をやめる 読者になる 読者になる

future::is_ready()がほしいケース

C++

C++11のドラフト段階では、std::futureにis_ready()というメンバ関数がありました。これは、非同期実行している関数の結果が設定され、値を取り出す準備ができたかどうかを判定する機能です。
C++11が正式に制定された段階では、インタフェースの複雑さ回避のため、is_ready()メンバ関数が削除されてしまいましたが、ここでその機能が必要なケースを挙げてみようと思います。


ここでは、ゲームループのような定期実行される処理の中で、別スレッドで計算を走らせるのにfutureを使用しています。
future::get()は値の準備ができるまでブロッキングしてしまうので、短い時間で定期実行される処理の中では呼びたくありません。そんなときに、futureの状態を問い合わせる関数があると便利です。
以下は、is_ready()のないstd::futureの代わりに、Boost.Threadのfutureを使用しています。

#define BOOST_THREAD_VERSION 3

#include <iostream>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <boost/optional.hpp>

namespace asio = boost::asio;
namespace chrono = boost::chrono;

class Game {
    bool calc_start_;
    boost::optional<int> result_;
    boost::future<int> async_calc_;
public:
    Game() : calc_start_(false) {}

    void update()
    {
        async_calc();
    }

    void draw()
    {
    }

private:
    // 非同期に計算を行う
    void async_calc()
    {
        // 3. 計算完了していたらそれ以降は何もしない
        if (result_)
            return;

        if (!calc_start_) {
            calc_start_ = true;

            // 1. 別スレッドで計算を開始する
            async_calc_ = boost::async(boost::launch::async, boost::bind(&Game::calc, this));
        }
        else {
            // 2. 計算完了したら結果を取り出す
            if (async_calc_.is_ready()) {
                result_ = async_calc_.get();

                std::cout << result_.get() << std::endl;
            }
        }
    }

    int calc() const
    {
        int sum = 0;
        for (int i = 0; i < 10; ++i) {
            sum += i + 1;
        }
        return sum;
    }
};

const chrono::milliseconds  
    timer_duration(static_cast<int>(1.0 / 60.0 * 1000));

void on_timer(Game& game, asio::steady_timer& timer)
{
    game.update();
    game.draw();

    timer.expires_from_now(timer_duration);
    timer.async_wait(
        boost::bind(&on_timer, boost::ref(game), boost::ref(timer)));
}

int main()
{
    Game game;

    asio::io_service io_service;
    asio::steady_timer timer(io_service);

    timer.expires_from_now(timer_duration);
    timer.async_wait(
        boost::bind(&on_timer, boost::ref(game), boost::ref(timer)));

    io_service.run();
}
55

標準の範囲内でfutureの状態を問い合わせたい場合は、std::future::wait_for()メンバ関数を使用できます。
wait_for()は戻り値として、futureの状態となる列挙値を返し、それにreadyも含まれていることから同じことができます。wait_for()は通常f.wait_for(seconds(3))のようにタイムアウト時間を指定して使いますが、f.wait_for(seconds(0))のようにして現在時間以下を指定することで、ブロッキングすることなくfutureの状態を取り出せます。


参照:
std::future::is_readyが無くなった理由 - melpon日記
std::future::wait_for() - cpprefjp
std::future::wait_until() - cpprefjp