Ответ
Нет, поток в C++ (и в общем случае) не может «перезапустить» себя в прямом смысле. Объект std::thread представляет собой единичный поток выполнения. Как только функция потока завершается (возвращает управление или выбрасывает исключение), поток завершает свою работу и переходит в состояние not joinable. Повторно использовать этот же объект std::thread для запуска новой функции нельзя — это вызовет исключение std::system_error.
Что можно сделать:
- Завершить текущий поток и создать новый с той же логикой. Это не перезапуск, а создание нового потока.
- Использовать цикл внутри функции потока, чтобы он не завершался, а ждал новых задач. Это классический подход для worker-потоков в пуле.
Пример 1: Ошибочная попытка «переиспользования»:
#include <thread>
#include <iostream>
void task() {
std::cout << "Task done.n";
}
int main() {
std::thread th(task);
th.join(); // Поток завершен
// Попытка запустить поток снова через тот же объект
th = std::thread(task); // ОШИБКА! Объект th после join() находится в невалидном состоянии.
// Правильно: нужно создать новый объект.
std::thread th2(task);
th2.join();
return 0;
}
Пример 2: Реализация worker-потока с циклом (более практичный подход):
#include <iostream>
#include <thread>
#include <atomic>
#include <queue>
#include <mutex>
#include <condition_variable>
class WorkerThread {
std::thread thread;
std::atomic<bool> running{true};
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable cv;
void loop() {
while (running) {
std::function<void()> task;
{
std::unique_lock lock(queue_mutex);
// Ждем, пока не появится задача или не придет сигнал на остановку
cv.wait(lock, [this]() { return !tasks.empty() || !running; });
if (!running && tasks.empty()) break;
task = std::move(tasks.front());
tasks.pop();
}
task(); // Выполняем задачу
}
std::cout << "Worker thread finished.n";
}
public:
WorkerThread() : thread(&WorkerThread::loop, this) {}
~WorkerThread() {
stop();
if (thread.joinable()) thread.join();
}
void stop() {
running = false;
cv.notify_all(); // Будим поток, чтобы он мог завершиться
}
void post_task(std::function<void()> task) {
{
std::lock_guard lock(queue_mutex);
tasks.push(std::move(task));
}
cv.notify_one(); // Сигнализируем потоку, что есть работа
}
};
В этом примере поток работает в цикле (loop()), обрабатывая множество задач, и завершается только по сигналу. Это эффективнее, чем постоянное создание и уничтожение потоков.
Ответ 18+ 🔞
Ёпта, ну слушай, тут народ часто спрашивает, можно ли поток в C++ как-то перезапустить, типа как старый телевизор, когда изображение поплыло — хлопнул по боку, и снова работает. Так вот, блядь, нет, нельзя. Объект std::thread — это как одноразовый шприц, ёб твою мать. Отработал свою функцию — и всё, пиши пропало, состояние у него теперь not joinable. Если попробуешь через тот же самый объект новую функцию запустить, он тебе такое std::system_error в морду выкинет, что мало не покажется. Доверия ебать ноль к такому подходу.
Что же делать, если очень хочется?
- Похоронить старый поток и родить новый. Это не перезапуск, а чистой воды создание нового потока, просто логика у него будет такая же.
- Сделать поток вечным работягой. То есть запихнуть в его функцию бесконечный цикл, чтобы он сидел, ждал заданий и не думал сдыхать. Это самый правильный и рабочий подход для всяких пулов потоков.
Пример 1: Как НЕ НАДО делать (классический косяк новичка):
#include <thread>
#include <iostream>
void task() {
std::cout << "Task done.n";
}
int main() {
std::thread th(task);
th.join(); // Поток отработал и лег костьми
// Попытка воскресить труп
th = std::thread(task); // ОШИБКА! Объект th после join() — это уже не поток, а так, **хуй в пальто**.
// Правильно — взять новый, свежий объект.
std::thread th2(task);
th2.join();
return 0;
}
Пример 2: Нормальный работяга-поток, который не ленится (практичный вариант):
#include <iostream>
#include <thread>
#include <atomic>
#include <queue>
#include <mutex>
#include <condition_variable>
class WorkerThread {
std::thread thread;
std::atomic<bool> running{true};
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable cv;
void loop() {
while (running) {
std::function<void()> task;
{
std::unique_lock lock(queue_mutex);
// Спим, пока не появится работа или пока нас не пошлют нахуй
cv.wait(lock, [this]() { return !tasks.empty() || !running; });
if (!running && tasks.empty()) break;
task = std::move(tasks.front());
tasks.pop();
}
task(); // Выполняем то, что дали
}
std::cout << "Worker thread finished.n";
}
public:
WorkerThread() : thread(&WorkerThread::loop, this) {}
~WorkerThread() {
stop();
if (thread.joinable()) thread.join();
}
void stop() {
running = false;
cv.notify_all(); // Кричим всем потокам: "Подъём, **хитрая жопа**, работа закончилась!"
}
void post_task(std::function<void()> task) {
{
std::lock_guard lock(queue_mutex);
tasks.push(std::move(task));
}
cv.notify_one(); // Тыкаем одного работягу: "Эй, **чувак**, дело есть!"
}
};
Вот в этом примере поток живёт в цикле (loop()), жрёт задачи одну за другой и кончает только когда ему явно скажут. Это в овердохуища раз эффективнее, чем постоянно плодить и хоронить потоки — терпения ноль ебать на такую возню.