TheLostJedi Ответов: 1

Почему этот метод закрытия рабочего потока не работает?


У меня есть приложение MFC, графический интерфейс которого имеет кнопку запуска и остановки. Кнопка "Пуск" вызывает функцию, которую я хочу периодически запускать в фоновом режиме. Мое решение состояло в том, чтобы использовать SetTimer функция для вызова OnTimer функционируйте через регулярные промежутки времени. Затем эта функция создает поток с помощью AfxBeginThread а затем заполняет структуру, которую я передаю потоку в качестве параметра. Все это прекрасно работает.

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

Что я уже пробовал:

Я создал int переменная называется count и установите его на 0 всякий раз, когда нажимается пуск, и установите его в 1 всякий раз, когда нажимается стоп.
Структура, в которую я перехожу AfxBeginThread выглядеть так
typedef struct THREADSTRUCT
            {
            MyGUIDlg*    _this;
	       int          flag;
            //you can add here other parameters you might be interested on
           } THREADSTRUCT;


При нажатии кнопки Пуск SetTimer вызывается функция, функция обратного вызова которой определена следующим образом:
void MyGUIDlg::OnTimer(UINT_PTR nIDEvent)
{
	THREADSTRUCT* _param = new THREADSTRUCT;
	_param->_this=this;
	_param->flag = count;
	AfxBeginThread(StartThread, _param);	
	CDialog::OnTimer(nIDEvent);
}


В моей функции StartThread я добавил следующее:

UINT MyGUIDlg::StartThread(LPVOID param)
{
    THREADSTRUCT *ts = (THREADSTRUCT*)param;
    if((ts->flag)==1)
	{
		//Use AfxEndThread to close this thread
	}

	return 0;
}


Но это, кажется, не работает, какие-нибудь мысли почему?

1 Ответов

Рейтинг:
10

Jochen Arndt

Рабочий поток обычно содержит цикл, в котором он проверяет наличие условия остановки:

UINT MyGUIDlg::StartThread(LPVOID param)
{
    THREADSTRUCT *ts = (THREADSTRUCT*)param;
    while (!ts->flag)
    {
        // Perform operations here
        // May also break the loop for self termination
    }
    // Thread is closed when it returns or when
    //  AfxEndThread() is called.
    // When m_bAutoDelete is TRUE, the thread object is deleted.
    return 0;
}
Вышеописанное опрашивает переменную общей переменной, чтобы проверить, должен ли поток завершиться. Лучшим решением является использование каких-то сигналов (событий).

Но в вашем примере нет петли, и поток всегда немедленно закрывается.

Я предлагаю почитать о рабочих потоках. Старая, но очень хорошая статья Использование Рабочих Потоков[^]. Смотреть также Многопоточность с помощью C++ и MFC[^] в MSDN.


TheLostJedi

Спасибо! Сама функция StartThread периодически вызывается SetTimer,поэтому я не ставил цикл. Но я собираюсь изучить использование сигналов для закрытия потока.

Jochen Arndt

Запуск потока с таймера-это плохой подход (я имел в виду упомянуть об этом в своем решении, но забыл). Он будет периодически запускать новый поток, так что у вас может быть несколько запущенных потоков. Затем вы должны использовать какой-то пул потоков для отслеживания состояния каждого потока, чтобы вы могли остановить их по отдельности (то есть иметь структуру потока для каждого потока). Если вы хотите запустить только один поток, то нет необходимости в таймере, и вы можете проверить состояние запущенных потоков перед запуском нового (что требует хранения указателя объекта потока).

Я не знаю о ваших требованиях (что вы делаете в потоке), но вы можете переосмыслить свой дизайн. Предоставленные ссылки должны помочь в этом.

TheLostJedi

Но разве оператор "return 0" не завершает поток каждый раз? Мне нужно периодически выполнять функцию, выполняемую потоком, поэтому я использовал SetTimer. Я думал об использовании функции SetTimer из одного потока, но не был уверен, как я передам ей функцию OnTimer?
Я все еще работаю над дизайном, так что я весь внимание на полезные изменения.

Jochen Arndt

На этом нить обрывается. Если это короткий запущенный поток, то вам обычно не нужно условие остановки (кроме того, при завершении приложения). Если это длинный запущенный поток, вы можете получить несколько потоков от таймера.

Если поток должен периодически выполнять какую-то работу, возможно, было бы лучше сделать синхронизацию внутри потока с помощью WaitForSingleObject или WaitForMultipleObjects с соответствующим значением тайм-аута. Эти функции ожидания также могут использоваться для воздействия на сигналы (события), такие как завершение потока, и выполнения работы до наступления тайм-аута.