18

RPC 通信プロトコルでは、メソッドの呼び出し後に「完了」メッセージを呼び出し元に送り返します。メソッドは並行して呼び出されるため、応答 (a std::string) を含むバッファーはミューテックスで保護する必要があります。私が達成しようとしているのは次のとおりです。

void connection::send_response()
{
    // block until previous response is sent
    std::unique_lock<std::mutex> locker(response_mutex_);

    // prepare response
    response_ = "foo";

    // send response back to caller. move the unique_lock into the binder
    // to keep the mutex locked until asio is done sending.
    asio::async_write(stream_,
                      asio::const_buffers_1(response_.data(), response_.size()),
                      std::bind(&connection::response_sent, shared_from_this(),
                                _1, _2, std::move(locker))
                      );
}

void connection::response_sent(const boost::system::error_code& err, std::size_t len)
{
    if (err) handle_error(err);
    // the mutex is unlocked when the binder is destroyed
}

ただし、ハンドラーが CopyConstructible であるboost::asio必要があるため、これはコンパイルに失敗します。

の代わりに次の共有ロッカー クラスを使用することで、問題を回避できます (あまりエレガントではありませんが) unique_lock

template <typename Mutex>
class shared_lock
{
public:
    shared_lock(Mutex& m)
    : p_(&m, std::mem_fn(&Mutex::unlock))
    { m.lock(); }

private:
    std::shared_ptr<Mutex> p_;
};

boost::asio移動のみのハンドラーを許可しない理由は何ですか?

4

2 に答える 2

13

Chris Kohlhoff が私が提出したバグに応答するまで、簡単な回避策があります。

template <typename F>
struct move_wrapper : F
{
    move_wrapper(F&& f) : F(std::move(f)) {}

    move_wrapper(move_wrapper&&) = default;
    move_wrapper& operator=(move_wrapper&&) = default;

    move_wrapper(const move_wrapper&);
    move_wrapper& operator=(const move_wrapper&);
};

template <typename T>
auto move_handler(T&& t) -> move_wrapper<typename std::decay<T>::type>
{
    return std::move(t);
}

ラッパーはコピー コンストラクターを宣言し、asio の機構をだまして送信させますが、決して定義しないため、コピーするとリンク エラーが発生します。

これで、最終的にこれを行うことができます:

std::packaged_task<int()> pt([] {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
});
std::future<int> fu = pt.get_future();

boost::asio::io_service io;
io.post(move_handler(pt));
std::thread(&boost::asio::io_service::run, &io).detach();

int result = fu.get();
assert(result == 42);
于 2014-04-06T08:11:29.537 に答える