работа с потоками c++

Я сделал синхронизацию работы двух потоков, создаваемых классом example каким-то супер костыльным методом (используя проверку с id), как мне правильно реализовать завершение работы 1 потока после запуска 2?

То есть, создаю объект класса example в main'е -> из конструктора запускается 1 поток, который с интервалом в 2 секунды выводит сообщение с id потока в консоль. main поток усыпляю на 10 секунд, после чего запускаю 2 поток. Т.к. 2 поток запущен, мне нужно чтобы 1 поток, проснувшись, сразу же завершил свою работу, не выполняя никаких действий внутри цикла. Наверное, теоретически правильным будет сделать это с помощью mutex, но я пока не понял как.

Результат, если не добавлять проверку с id (работает сразу 2 потока):

16596
16596
16596
16596
16596
9104
16596
9104
16596
9104
16596
9104
16596
9104
16596
9104

Результат (тот, что мне как раз нужен, созданный 2 поток уже не работает параллельно с 1, т.к. первый завершил свою работу, остановив цикл) с проверкой id:

18356
18356
18356
18356
18356
15788
15788
15788
15788
15788
#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>

class example
{
    std::atomic_bool m_do_loop_iteration;
    std::thread m_thread;
    std::mutex m_mutex;
    std::atomic_int m_id;

    void start()
    {
        m_id++;

        m_thread = std::thread([&]()
        {
            //std::lock_guard<std::mutex> lock(m_mutex);
            int id = m_id.load();

            while (do_loop_iteration())
            {
                if (id != m_id.load())
                {
                    break;
                }

                std::cout << std::this_thread::get_id() << std::endl;
                std::this_thread::sleep_for(std::chrono::seconds(2));
            }
        });
    }

public:
    example() :
        m_do_loop_iteration(true),
        m_id(0)
    {
        start();
    }

    bool do_loop_iteration()
    {
        return m_do_loop_iteration.load();
    }

    void restart()
    {
        m_thread.detach();
        start();
    }
};

int main()
{
    example ex;
    std::this_thread::sleep_for(std::chrono::seconds(10));
    ex.restart();

    std::this_thread::sleep_for(std::chrono::seconds(9999999));

    return 0;
}

Ответы (2 шт):

Автор решения: user7860670

mutex сам по себе позволяет только блокировать одновременный доступ нескольких потоков к участку кода. А вот condition_variable больше подходит для такого случая, так как позволяет ожидать чего-либо не нагораживая костылей из sleep или busy loop.

online compiler

#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>
#include <condition_variable>

class example
{
    ::std::thread m_thread;
    ::std::mutex m_mutex;
    ::std::condition_variable m_cond;
    bool m_loop;
    int m_counter;

    private: void start(void)
    {
        m_loop = true;
        m_thread = ::std::thread{&example::thread_routine, this};
    }

    private: void thread_routine(void)
    {
        ::std::unique_lock lock{m_mutex};
        do
        {
            ::std::cout << m_counter << ::std::endl;
        }
        while
        (
            not m_cond.wait_for
            (
                lock
            ,   ::std::chrono::seconds{1}
            ,   [this](void)
                {
                    return not m_loop;
                }
            )
        );
    }

    private: void stop(void)
    {
        {
            ::std::lock_guard lock{m_mutex};
            m_loop = false;
        }
        m_cond.notify_one();
        m_thread.join();
        ++m_counter;
    }

    public: example(void): m_loop{}, m_counter{}
    {
        start();
    }

    public: ~example(void)
    {
        stop();
    }

    public: void restart(void)
    {
        stop();
        start();
    }
};

int main()
{
    example ex{};
    for (;;)
    {
        ::std::this_thread::sleep_for(::std::chrono::seconds{3});
        ex.restart();
    }
    return 0;
}
→ Ссылка
Автор решения: daniel
class Gateway
{
    std::thread m_thread;
    std::condition_variable m_cond_var;
    std::mutex m_mutex;
    bool m_connected;

    void connect();

public:
    void on_close(bool reconnect = false);
    bool is_connected() const;

    Gateway();
    ~Gateway();
};

void Gateway::connect()
{
    std::cout << "Gateway::connect - start" << std::endl;

    {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_connected = true;
    }

    m_thread = std::thread([&]()
    {
        std::unique_lock<std::mutex> lock(m_mutex);

        while (is_connected())
        {
            std::cout << "ping " << std::this_thread::get_id() << std::endl;
            m_cond_var.wait_for(lock, std::chrono::seconds(2), [&]() { return not is_connected(); });
        }
    });

    std::cout << "Gateway::connect - end" << std::endl;
}

void Gateway::on_close(bool reconnect)
{
    std::cout << "Gateway::on_close - start" << std::endl;

    if (is_connected())
    {
        {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_connected = false;
        }

        m_cond_var.notify_one();
        m_thread.join();
    }

    if (reconnect)
    {
        connect();
    }

    std::cout << "Gateway::on_close - end" << std::endl;
}

bool Gateway::is_connected() const
{
    return m_connected;
}

Gateway::Gateway() :
    m_connected(false)
{
    connect();
}

Gateway::~Gateway()
{
    on_close();
}

int main()
{
    std::cout << "main - start" << std::endl;

    {
        Gateway gateway;
        std::this_thread::sleep_for(std::chrono::seconds(10));
        gateway.on_close(true);
        std::this_thread::sleep_for(std::chrono::seconds(20));
    }

    std::cout << "main - end" << std::endl;

    return 0;
}
→ Ссылка