C ++ Boost,等待来自不同线程的var init

时间:2018-07-29 18:26:26

标签: c++ multithreading networking boost

我的TCP客户端类具有将请求发送到服务器的方法。 在某些情况下,我需要等待响应才能发出另一个请求。 当然,我可以这样写:

class MyClass {
private:
    SomeResponsMsgType* some_resp_msg_type;
    TCP_client tcp_client;

    void handle_resp(T resp) {
        //handles responses
        //initializes some_resp_msg_type and others
    }

public:
    MyClass() : some_resp_msg_type(nullptr) {}

    void init() {
        tcp_client.run(this, MyClass::handle_resp);
    }

    SomeResponsMsgType* make_request(int v) {
        Request req(v);

        some_resp_msg_type = nullptr;

        send_message(req);

        int timeout = 10;
        while(!some_resp_msg_type && !timeout) {
            Sleep(100);
            --timeout;
        }
        SomeResponsMsgType* ret = some_resp_msg_type;
        some_resp_msg_type = nullptr;

        return ret;
    }
};

//and use it like this:

void foo() {
    MyClass my_class;
    my_class.init();
    SomeResponsMsgType* resp = my_class.make_request(1);
    if(!resp)
        ...

    AnotherResponsMsgType* resp2 = my_class.make_another_request(resp->some_var);
}

但是此代码看起来丑陋且不安全。 请帮我举个例子。谢谢。

1 个答案:

答案 0 :(得分:0)

确实,我会在这里查看期货,并且更喜欢让调用者决定何时等待该值:

#include <boost/asio.hpp>
#include <iostream>
namespace ba = boost::asio;

using ba::ip::tcp;
using boost::system::error_code;
using namespace std::chrono_literals;

struct Request {
    Request(int v) : _v(v) {}
    int _v;

    friend std::ostream& operator<<(std::ostream& os, Request const& req) {
        return os << req._v;
    }
};

struct SomeResponseMsgType {
    std::string text;
};

class MyClass {
    std::future<SomeResponseMsgType> send_message(Request const& req) {
        std::promise<SomeResponseMsgType> p;

        try {
            // This would be your async call. Here I simplify by 
            tcp::iostream sock(tcp::endpoint{ {}, 6767 });
            sock.exceptions(std::ios::failbit | std::ios::eofbit | std::ios::badbit);

            sock.expires_after(1500ms);
            sock << req << "\n";

            {
                std::ostringstream oss;
                oss << sock.rdbuf();
                p.set_value(SomeResponseMsgType {oss.str()});
            }
        } catch (...) {
            p.set_exception(std::current_exception());
        }

        return p.get_future();
    }

public:
    std::future<SomeResponseMsgType> make_request(int v) {
        return send_message(Request{v});
    }
};

//and use it like this:

int main() {
    MyClass my_class;
    auto f1 = my_class.make_request(1);
    auto f2 = my_class.make_request(2);

    try {
        {
            SomeResponseMsgType resp = f2.get();
            std::cout << "Second request gave: " << resp.text << "\n";
        }
        {
            SomeResponseMsgType resp = f1.get();
            std::cout << "First request gave: " << resp.text << "\n";
        }
    } catch(boost::system::system_error const& e) {
        std::cerr << "Whoops " << e.code().message() << "\n";
    }
}

当然,在您的示例中send_message可能会以某种异步方式使用TCP_Client,但是原理保持不变。要实现更多自动化,请参见packaged_task<>

请注意,此处保证了请求的顺序(因为send_message实际上是同步的)。使用异步代码,由send_message安排的异步操作的排队将决定是否按顺序发送请求。 (然后,如果服务器碰巧响应更快,那么第二个未来可能实际上要比第一个更早完成。)

对上述内容的愚蠢测试:

socat TCP4-LISTEN:6767,reuseaddr,fork "SYSTEM:sleep 1 && /bin/date"&
./sotest 
Second request gave: ma 30 jul 2018  1:14:42 CEST

First request gave: ma 30 jul 2018  1:14:41 CEST