Я создал класс С++ с именем Timer, у него есть 3 метода:
Метод start()
включает флаг с именем run
, присваивая ему значение true
.
Метод stop()
, отключите флаг, установив его значение в false
.
Метод print()
выполняет while()
при условии, если run == true
, и печатает некоторый текст, ожидающий полсекунды.
Таймер.hpp
#ifndef TIMER
#define TIMER 1
#include <iostream>
#include <cstdbool>
#include <unistd.h>
class Timer{
private:
bool run;
public:
void start();
void stop();
void print();
};
#endif
Таймер.cpp
#include "Timer.hpp"
void Timer::start(){
this->run = true;
this->print();
return;
}
void Timer::stop(){
this->run = false;
return;
}
void Timer::print(){
int counter = 0;
while(this->run == true){
std::cout << counter << std::endl;
counter++;
usleep(500000);
}
return;
}
main.cpp
#include <pthread.h>
#include "Timer.hpp"
void *handler(void *argument){
((Timer *) argument)->start();
return argument;
}
int main(void){
Timer *timer = new Timer();
pthread_t timer_thread;
int mainCounter = 0;
pthread_create(&timer_thread, NULL, handler, (void *) &timer);
while(true){
if (mainCounter == 100){
std::cout << "Stopping..." << std::endl;
timer->stop();
}
std::cout << " => " << mainCounter << std::endl;
mainCounter++;
usleep(50000);
}
return 0;
}
Моя проблема.
Я создал поток для обработки выполнения метода start()
, после того как я создал условие внутри основного потока, в котором после mainCounter
выполнения 100 итераций он выполняет timer->stop()
, но не останавливает цикл таймера.
Когда mainCounter
достигает 100-й итерации, он не может остановить цикл внутри потока.
Инструкция по компиляции:
g++ Timer.cpp -c
g++ Timer.cpp main.cpp -o main -lpthread
Результат:
9
=> 90
=> 91
=> 92
=> 93
=> 94
=> 95
=> 96
=> 97
=> 98
=> 99
10
Stopping...
=> 100
=> 101
=> 102
=> 103
=> 104
=> 105
=> 106
=> 107
=> 108
=> 109
11
Знаете ли вы, что никогда не выходите из цикла в вашей основной теме?
@ r3musn0x Я знаю, что никогда не выходил из основного потока, проблема в том, что я хочу остановить цикл внутри потока.
@IvanBotero, из предоставленного вами вывода кажется, что основной поток продолжает печать, а вторичный поток, возможно, уже завершился.
Опечатка-> (void *) &timer)
таймер уже является указателем. Вы передаете указатель на указатель и используете его (((Timer *) argument)->start();
) как простой указатель. Потерять &
Вы фактически остановили рабочий поток. Это основной поток, который продолжает работать, печатая эти значения.
Рабочий поток @Timmy_A, вероятно, не был остановлен. this
внутри Timer::print
не указывает на то же самое, что и timer
@ user4581301 Конечно. Пользователь отредактировал свой вывод, когда я писал свой ответ.
Попробуйте пометить поле участника запуска ключевым словом volatile. Но настоящую ошибку @user4581301 уже описал. Проблема заключается в использовании указателя на указатель вместо указателя. В рабочем потоке вы изменяете другую память, а не ту, которая принадлежит переменной «таймер». Таким образом, для «запуска» никогда не устанавливается значение «истина».
Спасибо за ваши комментарии, я добавил пример поверх ideone.com.
Как уже упоминал @user4581301, это решит проблему:
pthread_create(&timer_thread, NULL, handler, (void *) &timer);
должно быть
pthread_create(&timer_thread, NULL, handler, (void *) timer);
Проблема в том, что timer
указывает на выделенный Timer
класс, а &timer
указывает где-то в стеке. Во время выполнения потока вы пытаетесь получить доступ к члену класса run
, но поскольку this
указывает на стек, вы на самом деле читаете неверное значение.
Другие примечания: объявите bool run
как std::atomic_bool run
или используйте любой механизм мьютекса для потокобезопасности. Кроме того, всегда удаляйте выделенные вами переменные: delete timer
.
Лучшая формулировка: от указывает где-то в стеке до указывает на себя timer
и уже является указателем., потому что мы точно знаем, куда он указывает. Есть немного более разумная альтернатива: во-первых, не используйте динамическое размещение: Timer timer;
Тогда вы сможете (void *) &timer
, timer.stop();
и не утечь timer
, потому что это не было delete
d. Что касается атомной проблемы, я хакер, поэтому я бы, вероятно, использовал volatile sig_atomic_t run;
Если вы собираетесь читать и записывать переменную (например, вашу логическую переменную
run
) из нескольких потоков, вам нужно либо защитить все обращения к этой переменной с помощью мьютекса, либо сделать переменную атомарной с помощьюstd::atomic
. В противном случае вы вызываете неопределенное поведение, и ваша программа может не делать того, что вы от нее ожидаете. (см.: stackoverflow.com/questions/31978324/что именно-это-stdatomic )