работа с потоками 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 шт):
mutex сам по себе позволяет только блокировать одновременный доступ нескольких потоков к участку кода. А вот condition_variable больше подходит для такого случая, так как позволяет ожидать чего-либо не нагораживая костылей из sleep или busy loop.
#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;
}
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;
}