Обратный вызов потока не запускается, когда я создаю `std::jthread` как локальную переменную внутри метода, почему?

Я изо всех сил пытаюсь заново выучить C++ и сейчас играю с jthread.
Кажется, у меня все работает при вызове функции-члена класса, которую я хочу, в отдельном потоке.
Однако на самом деле функция никогда не вызывается. Я не могу понять, чего мне здесь не хватает.
Если кто-то сможет указать на мою неизбежную ошибку «ученика», я буду очень признателен.

// includes and directives

#include <iostream>
#include <thread>
#include <functional>
#include <chrono>

// a class for testing stuff

class A {
private:
    int updates{ 0 }; // something to count on
    int tick{ 1000 }; // tick time set to 1000ms initially

public:
    void set_tick(int t) {
        tick = (t<1?1:t); // sanity check and set tick time
    }

    int get_updates() {
        return updates; // I did how many?
    }

    void update(std::stop_token st) {
        while (!st.stop_requested()) { // stop if they say stop
            updates++; // count this as an update
            std::cout << "Performing update" << updates << "\n"; // visually indicate something happened
            std::this_thread::sleep_for(std::chrono::milliseconds(tick)); // wait for tick time before doing it all again
        }
    }

    void start() { // start a ticking function
        std::cout << "Starting update thread\n"; // tell someone responsible that it's started
        std::jthread thr_A(std::bind_front(&A::update, this)); // start the parallel process fucntion in this class
    }
};

// main thing

int main()
{
    std::cout << "Start main thread\n"; // say that I'm starting up 
    A a; // make me a sandwich
    a.set_tick(10); // really fast(ish)
    a.start(); // go on, get on with it
    std::this_thread::sleep_for(std::chrono::milliseconds(10000)); // wait until sandwich ready
    std::cout << "Wake main thread and terminate\n"; // sandwich is ready, time to eat
}

thr_A сразу же отвлекается, когда start возвращается. Поскольку это jthread, его просят остановиться и joined. У него едва ли есть шанс побежать.
wohlstad 01.06.2024 09:27

Ваш экземпляр jthread выходит из области действия, как только start достигает своего конца. Затем автоматически вызывается деструктор jthread, который, в свою очередь, вызывает член request_stop и join на нем. Это потому, что thr_A — это автоматический объект, локальный для start.

Red.Wave 01.06.2024 09:36

Примечание: альтернативой использованию std::bind является использование лямбда-выражения . Например. std::jthread thread{[this]{ update(); };. Лямбда-выражения также часто используются в сочетании с алгоритмами стандартной библиотеки.

Pepijn Kramer 01.06.2024 10:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

thr_A определяется как локальная переменная в start().
Он уничтожается, когда выходит за пределы области действия, т.е. когда start() возвращается.
Поскольку это jthread, при уничтожении его просят остановить, а затем joined.
Поскольку start() возвращается сразу после запуска thr_A, у потока практически нет возможности запуститься.

Возможным решением может быть добавление thr_A в качестве члена class A. Это означает, что его деструктор будет вызываться только тогда, когда ваш a будет уничтожен — в вашем случае: когда main() выйдет.

Таким образом, поток выполнит всю ожидаемую вами логику.

Live demo

Примечание: в живой демонстрации я уменьшил sleep_for в main(), чтобы избежать тайм-аута Godbolt.

Я знал, что где-то это будет ошибкой новичка. Теперь я точно понимаю, что вы имеете в виду! Спасибо всем за помощь! Да, я тоже буду переписывать, используя лямбда-выражение, чтобы снова во всем разобраться.

Radiobiscuit 01.06.2024 12:40

@Radiobiscuit рад помочь! И да, лямбда-выражения — это очень полезно, и их стоит изучить.

wohlstad 01.06.2024 12:42

Другие вопросы по теме