简体   繁体   中英

How to properly embed QEventLoop in the std::thread?

There is an object that contains std::thread that I want to be finished when the object is destroyed.

Minimal working code:

#include <thread>
#include <future>
#include <QEventLoop>

struct Connector
{
    Connector(std::string addr, std::function<void(std::string)> cb)
    {
        std::promise<void> barrier;
        auto fut = barrier.get_future();

        m_worker = std::thread([addr, cb, &barrier]()
        {
            QEventLoop loop;
            QTimer::singleShot(0, [this, &barrier, &loop]
            {
                m_quit = [&loop] { QTimer::singleShot(0, &loop, &QEventLoop::quit); };
                barrier.set_value();
            });

            MySocket s(addr, cb);

            loop.exec();
        });

        fut.wait();
    }

    ~Connector()
    {
        m_quit();
        m_worker.join();
    }

    std::thread worker;
    std::function<void()> m_quit;
};

It becomes complex very fast: you can call exit() on the loop only after it enters the exec() , you can't create the loop outside of the thread.

I only have a solution with a semaphore that is released by a handler that is queued for the execution in this loop. When the semaphore is released I can be sure that the loop is created and running, so it can be terminated with a quit() message when needed.

Am I missing a simpler way?

Maybe you could pass a reference to a unique_ptr of QEventLoop to the thread and at destruction call exit on that pointer. Like this:

#include <thread>
#include <QEventLoop>

struct Connector
{
    Connector()
    {
        m_worker = std::thread([=]()
        {
            event_loop = std::make_unique<QEventLoop>();
            loop->exec();
        });
    }

    ~Connector()
    {
        event_loop->exit();
        m_worker.join();
    }

    std::unique_ptr<QEventLoop> event_loop;
    std::thread worker;
};

Here's my take on it, based on katrasnikj's answer and std::promise docs to ensure that the thread is running by the time the constructor finishes.

struct Connector
{
    Connector()
    {
        std::promise<void> barrier;
        auto fut = barrier.get_future();
        worker = std::thread([=](std::promise<void>&& barrier)
        {
            event_loop = std::make_unique<QEventLoop>();
            barrier.set_value();
            event_loop->exec();
        }, std::move(barrier));
        fut.wait();
    }

    ~Connector()
    {
        QTimer::singleShot(0, event_loop.get(), &QEventLoop::quit);
        worker.join();
    }

    std::unique_ptr<QEventLoop> event_loop;
    std::thread worker;
};

But you might want to look into using QThread instead, since it's capable of running its own event loop.

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