Не подскажете ли, уважаемые коллеги, как лучше всего работать с потоками (threads) в MFC?
Есть функция, производящая математические вычисления. Эта функция большая по длине и её действия происходят в цикле до выполнения некоего условия. Эта функция работает в консольном режиме--ввод и вывод данных там происходит с помощью консольного окна.
Как лучше поместить эту функцию в поток--использовать класс CWinThread, воспользоваться функцией AfxBeginThread или же использовать функции WinAPI для работы с потоками?
Здравствуйте, RussianFellow, Вы писали:
RF>Здравствуйте, _Butch_, Вы писали:
_B_>>std::thread
RF>А если функция, которую я хочу поместить в поток, является членом класса--то как быть?
#include <iostream>
#include <chrono>
#include <thread>
class Dumb
{
private:
int id;
public:
Dumb(int _id)
: id(_id)
{
}
void stupid()
{
for (int i = 0; i < 10; i++)
{
using namespace std::chrono_literals;
std::cout << id << " is thinking..." << std::endl;
std::this_thread::sleep_for(1s);
}
}
};
int main()
{
Dumb d1(1);
std::thread t1(&Dumb::stupid, &d1);
Dumb d2(2);
std::thread t2(&Dumb::stupid, &d2);
t1.join();
t2.join();
return 0;
}
Ещё у меня такой вопрос--как работать с классом CWinThread?
Как привязать некую функцию к потоку CWinThread? (Меня интересует как обычная функция, так и функция-член класса).
Надо ли создавать класс-потомок от CWinThread для того, чтобы в нём выполнялась некая функция, или же достаточно самого класса CWinThread?
Если же нужно создавать класс-потомок от CWinThread, то какие методы там следует переопределять?
И как уничтожить поток? (Для создания потока используется функция CWinThread::Create, для запуска--функция CWinThread::SuspendThread, для продолжения работы потока--функция CWinThread::ResumeThread, а как принудительно уничтожить поток, не дожидаясь завершения его выполнения?)
Здравствуйте, RussianFellow, Вы писали:
RF>Ещё у меня такой вопрос--как работать с классом CWinThread?
Не заморачивайся с CWinThread — для рабочих потоков оно того не стоит.
AfxBeginThread, CreateThread, std::thread — на выбор
Вкратце почему не нужен CWinThread — там внутри создаётся поток и крутится цикл сообщений, как у основного потока приложения. Переопределять нужно виртуальную функцию Run. Но что бы поток завершился, просто выйти из этой функции недостаточно. Поток будет жить, пока не получит сообщение WM_QUIT, далее он автоматически самоубьется и грохнет все свои данные. Если есть какие-то переменные члены которые нужны после завершения работы, надо перед созданием ставить переменную bAutoDelete = FALSE и руками освобождать память.
В общем для простого потока "что-то посчитать в фоне" слишком много ненужных танцев с бубном.
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>Здравствуйте, RussianFellow, Вы писали:
RF>>Ещё у меня такой вопрос--как работать с классом CWinThread?
ES>Не заморачивайся с CWinThread — для рабочих потоков оно того не стоит. ES>AfxBeginThread, CreateThread, std::thread — на выбор
Хорошо, а как тогда при использовании AfxBeginThread или CreateThread приостановить поток, потом запустить его на продолжение, как уничтожить поток?
Не могли бы Вы привести примеры (простейшие), как использовать AfxBeginThread и CreateThread или дать ссылку на эти примеры?
Здравствуйте, RussianFellow, Вы писали:
RF> Хорошо, а как тогда при использовании AfxBeginThread или CreateThread приостановить поток, потом запустить его на продолжение,
Можно создать поток изначально остановленным установив флаг dwCreationFlags в CREATE_SUSPENDED
Продолжить выполнение — ResumeThread
Приостановить — SuspendThread
RF> как уничтожить поток?
Штатное завершение — просто выйти из функции потока.
Аварийное — TerminateThread
Желательно вызвать CloseHandle после окончания работы потока
Подробная информация
RF> Не могли бы Вы привести примеры (простейшие), как использовать AfxBeginThread и CreateThread или дать ссылку на эти примеры?
Здравствуйте, aloch, Вы писали:
A>Здравствуйте, RussianFellow, Вы писали:
RF>>Не могли бы Вы привести примеры (простейшие), как использовать AfxBeginThread и CreateThread или дать ссылку на эти примеры?
A>Рихтера про потоки почитайте, прежде чем во все это влезать.
Здравствуйте, RussianFellow, Вы писали:
RF>Как книга называется?
Дж. Рихтер. Windows для профессионалов. Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows. Изд-ва: Питер, Русская Редакция, 2001 г., 752 стр. ISBN 5-272-00384-5, 1-57231-996-8
Здравствуйте, RussianFellow, Вы писали:
RF>Не подскажете ли, уважаемые коллеги, как лучше всего работать с потоками (threads) в MFC?
_beginthreadex — в MSDN смотрим;
На крайний случай — AfxBeginThread (смотрим там же)
RF>Есть функция, производящая математические вычисления. Эта функция большая по длине и её действия происходят в цикле до выполнения некоего условия. Эта функция работает в консольном режиме--ввод и вывод данных там происходит с помощью консольного окна.
Условие — контролируем вызовом WinAPI-шной ::WaitForSingleObject
ну или же ::WaitForMultipleObjects — здесь зависит от того, имеется одно условие выхода из цикла или несколько.
RF>Как лучше поместить эту функцию в поток--использовать класс CWinThread, воспользоваться функцией AfxBeginThread или же использовать функции WinAPI для работы с потоками?
Правильнее всего — НЕ завязываться на CWinThread (избыточен он для данной задачи), вызывать _beginthreadex либо AfxBeginThread.
ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической,
если эта функция — просто метод класса, то данное решение работать НЕ БУДЕТ!
Здравствуйте, AlexGin, Вы писали:
AG>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической, AG>если эта функция — просто метод класса, то данное решение работать НЕ БУДЕТ!
Ясно.
И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса?
Здравствуйте, RussianFellow, Вы писали:
RF>Здравствуйте, AlexGin, Вы писали:
AG>>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической, AG>>если эта функция — просто метод класса, то данное решение работать НЕ БУДЕТ!
RF>Ясно.
RF>И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса?
Или указатель на поток (переменная типа HANDEL* или CWinThread*)?
Здравствуйте, RussianFellow, Вы писали:
RF>>И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса? RF>Или указатель на поток (переменная типа HANDEL* или CWinThread*)?
Здравствуйте, RussianFellow, Вы писали:
RF>>И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса? RF>Или указатель на поток (переменная типа HANDEL* или CWinThread*)?
Может, конечно же, данная переменная быть членом класса.
Я упоминал только тот факт, что головная функция рабочего потока — не бывает членом класса.
Как я понял, для запуска потока в MFC следует воспользоваться функциями AfxBeginThread или CreateThread или _beginthreadex, для приостановки выполнения потока следует воспользоваться функцией SuspendThread, для продолжения выполнения потока--функцией ResumeThread.
А как грамотно уничтожить поток в MFC?
Мне посоветовали функцию TerminateThread, но у неё есть недостатки--после её вызов поток всё равно занимает стек памяти и имеет доступ к ядру до своего завершения.
А как грамотно уничтожить поток, чтобы у него после этого уничтожения--чтобы после этого уничтожения этот поток не выполнялся, не имел доступа к ядру и не занимал стек памяти?
Есть функции AfxEndThread и ExitThread, вызываемые внутри функции потока для его завершения. Но как правильно их использовать?
И как правильно посылать сообщения потоку и как правильно поток должен принимать и обрабатывать эти сообщения?
Здравствуйте, RussianFellow, Вы писали:
RF>Есть функции AfxEndThread и ExitThread, вызываемые внутри функции потока для его завершения. Но как правильно их использовать? RF>И как правильно посылать сообщения потоку и как правильно поток должен принимать и обрабатывать эти сообщения?
Посмотрите, как сделан дотнетовский CancellationToken и сделайте так же. В ходе вычислений поток может проверять состояние некоего volatile флага, и если он взведён, то завершать вычисление, прерывать цикл, рекурсию, и т.д. В общем случае, ничего уничтожать не надо. Поток сам завершится, когда выйдет из начальной функции, после чего его хэндл можно будет проверить чем-то вроде WaitForSingleObject с нулевым таймаутом и по коду возврата узнать, что поток завершён, после чего закрыть хэндл. Результат работы потока можно сложить в некую общую переменную, прогресс его работы можно тоже записывать в volatile переменную и периодически проверять из основного потока — по таймеру, например.