简体   繁体   中英

Design of multi-threaded networking notifications in C++

Basically there are a couple operational modes that my program can be in, each of them requiring initialization then updating. Each mode has its own configuration struct.

How can I after receiving a message and determining what mode its describing notify the main thread to initialize then start updating that mode? (I'm looking for an outline of the best way to do it in C++17)

To clarify, it is a daemon application. After initialization, the main thread's only job is to do the calculations required by mode updates. The networking is done on a separate worker thread with Boost.Asio. I also expect (as I am writing the client side program as well) that the frequency of mode changes will be low (~10hz max), so I want to avoid the use of a queue since the modes are for controlling the behavior of a robot, and a queue would add latency.

Assuming your main thread can just sit and wait for the network download to complete before it starts the main code, you can use a std::condition_variable

Your main thread does this:

std::condition_variable cv;
std::mutex mut;
bool networkOperationCompleted;
Config globalconfig;

int main()
{
     // initialize whatever kicks off the network thread

     std::unique_lock<std::mutex> lck(mut);
     while (networkOperationCompleted == false)
     {
         cv.wait();
     }
}

meanwhile, your network thread does this:

extern std::condition_variable cv;
extern std::mutex mut;
extern bool networkOperationCompleted;
extern Config globalconfig;

void background_task()
{

     Config config;
     DownloadTheConfig(&config);
     {
         std::unique_lock<std::mutex> lck(m);
         globalConfig = config;
         networkOperationComplete = true;
         cv.notify_all();
     }
}
 

I ended up going with something like this:

class config
{
public:
    using empty = std::monostate;

    struct modeA
    {
        ...
    };

    struct modeB
    {
        ...
    };

    template<typename T, typename Lambda>
    void handle(Lambda&& handler) const
    {
        if (std::holds_alternative<T>(storage))
        {
            handler(std::get<T>(storage));
        }
    }

    void clear()
    {
        storage = empty{};
    }

private:
    std::variant<empty, modeA, modeB> storage;
};

config server::pop_cfg()
{
    std::unique_lock lock{cfg_mutex};
    config copy{cfg};
    cfg.clear();
    return copy;
}

int main()
{
    ...

    mode* mode;
    while (true)
    {
        config cfg = server.pop_cfg();
        
        cfg.handle<config::empty>([&](...) {
            mode->update();
        });
        
        cfg.handle<config::modeA>([&](config::modeA cfg) {
            mode = new ModeA(cfg);
        });
        
        cfg.handle<config::modeB>([&](config::modeB cfg) {
            mode = new ModeB(cfg);
        });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM