Работа с потоками (threads) в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 20.12.16 12:08
Оценка:
Не подскажете ли, уважаемые коллеги, как лучше всего работать с потоками (threads) в MFC?

Есть функция, производящая математические вычисления. Эта функция большая по длине и её действия происходят в цикле до выполнения некоего условия. Эта функция работает в консольном режиме--ввод и вывод данных там происходит с помощью консольного окна.
Как лучше поместить эту функцию в поток--использовать класс CWinThread, воспользоваться функцией AfxBeginThread или же использовать функции WinAPI для работы с потоками?
1613 г. = 2024 г.
Re: Работа с потоками (threads) в MFC
От: _Butch_  
Дата: 20.12.16 12:41
Оценка:
std::thread
Re[2]: Работа с потоками (threads) в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 20.12.16 13:13
Оценка:
Здравствуйте, _Butch_, Вы писали:

_B_>std::thread


А если функция, которую я хочу поместить в поток, является членом класса--то как быть?
1613 г. = 2024 г.
Re[3]: Работа с потоками (threads) в MFC
От: _Butch_  
Дата: 20.12.16 13:48
Оценка:
Здравствуйте, 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;
}
Re: Работа с классом CWinThread
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 20.12.16 14:29
Оценка:
Ещё у меня такой вопрос--как работать с классом CWinThread?
Как привязать некую функцию к потоку CWinThread? (Меня интересует как обычная функция, так и функция-член класса).
Надо ли создавать класс-потомок от CWinThread для того, чтобы в нём выполнялась некая функция, или же достаточно самого класса CWinThread?
Если же нужно создавать класс-потомок от CWinThread, то какие методы там следует переопределять?
И как уничтожить поток? (Для создания потока используется функция CWinThread::Create, для запуска--функция CWinThread::SuspendThread, для продолжения работы потока--функция CWinThread::ResumeThread, а как принудительно уничтожить поток, не дожидаясь завершения его выполнения?)
1613 г. = 2024 г.
Re[2]: Работа с классом CWinThread
От: Evgeniy Skvortsov Россия  
Дата: 20.12.16 15:14
Оценка: +1
Здравствуйте, RussianFellow, Вы писали:

RF>Ещё у меня такой вопрос--как работать с классом CWinThread?


Не заморачивайся с CWinThread — для рабочих потоков оно того не стоит.
AfxBeginThread, CreateThread, std::thread — на выбор

Вкратце почему не нужен CWinThread — там внутри создаётся поток и крутится цикл сообщений, как у основного потока приложения. Переопределять нужно виртуальную функцию Run. Но что бы поток завершился, просто выйти из этой функции недостаточно. Поток будет жить, пока не получит сообщение WM_QUIT, далее он автоматически самоубьется и грохнет все свои данные. Если есть какие-то переменные члены которые нужны после завершения работы, надо перед созданием ставить переменную bAutoDelete = FALSE и руками освобождать память.

В общем для простого потока "что-то посчитать в фоне" слишком много ненужных танцев с бубном.
Re[3]: Работа с классом CWinThread
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 21.12.16 06:12
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Здравствуйте, RussianFellow, Вы писали:


RF>>Ещё у меня такой вопрос--как работать с классом CWinThread?


ES>Не заморачивайся с CWinThread — для рабочих потоков оно того не стоит.

ES>AfxBeginThread, CreateThread, std::thread — на выбор

Хорошо, а как тогда при использовании AfxBeginThread или CreateThread приостановить поток, потом запустить его на продолжение, как уничтожить поток?
Не могли бы Вы привести примеры (простейшие), как использовать AfxBeginThread и CreateThread или дать ссылку на эти примеры?
1613 г. = 2024 г.
Re[4]: Работа с классом CWinThread
От: aloch Россия  
Дата: 21.12.16 07:39
Оценка: 6 (1) +2
Здравствуйте, RussianFellow, Вы писали:


RF>Не могли бы Вы привести примеры (простейшие), как использовать AfxBeginThread и CreateThread или дать ссылку на эти примеры?


Рихтера про потоки почитайте, прежде чем во все это влезать.


Re[4]: Работа с классом CWinThread
От: Evgeniy Skvortsov Россия  
Дата: 21.12.16 07:55
Оценка: 2 (1)
Здравствуйте, RussianFellow, Вы писали:

RF> Хорошо, а как тогда при использовании AfxBeginThread или CreateThread приостановить поток, потом запустить его на продолжение,

Можно создать поток изначально остановленным установив флаг dwCreationFlags в CREATE_SUSPENDED
Продолжить выполнение — ResumeThread
Приостановить — SuspendThread

RF> как уничтожить поток?

Штатное завершение — просто выйти из функции потока.
Аварийное — TerminateThread

Желательно вызвать CloseHandle после окончания работы потока

Подробная информация

RF> Не могли бы Вы привести примеры (простейшие), как использовать AfxBeginThread и CreateThread или дать ссылку на эти примеры?


Как обычно
Re[5]: Работа с классом CWinThread
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 21.12.16 08:18
Оценка:
Здравствуйте, aloch, Вы писали:

A>Здравствуйте, RussianFellow, Вы писали:



RF>>Не могли бы Вы привести примеры (простейшие), как использовать AfxBeginThread и CreateThread или дать ссылку на эти примеры?


A>Рихтера про потоки почитайте, прежде чем во все это влезать.


Как книга называется?
1613 г. = 2024 г.
Re[6]: Работа с классом CWinThread
От: Evgeniy Skvortsov Россия  
Дата: 21.12.16 08:41
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Как книга называется?


Дж. Рихтер. Windows для профессионалов. Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows. Изд-ва: Питер, Русская Редакция, 2001 г., 752 стр. ISBN 5-272-00384-5, 1-57231-996-8
Re[5]: Работа с классом CWinThread
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 22.12.16 06:15
Оценка:
И ещё вопрос: как поместить в поток функцию, которая принимает не один, а скажем, четыре параметра?
1613 г. = 2024 г.
Re[6]: Работа с классом CWinThread
От: Evgeniy Skvortsov Россия  
Дата: 22.12.16 06:46
Оценка: 4 (1)
Здравствуйте, RussianFellow, Вы писали:

RF>И ещё вопрос: как поместить в поток функцию, которая принимает не один, а скажем, четыре параметра?


Передавай все параметры через LPVOID lpParameter, структуру создай, запихай в неё все параметры и адрес этой структуры отдавай функции потока.
Re: Работа с потоками (threads) в MFC
От: AlexGin Беларусь  
Дата: 22.12.16 18:07
Оценка: 4 (1)
Здравствуйте, RussianFellow, Вы писали:

RF>Не подскажете ли, уважаемые коллеги, как лучше всего работать с потоками (threads) в MFC?

_beginthreadex — в MSDN смотрим;
На крайний случай — AfxBeginThread (смотрим там же)

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

Условие — контролируем вызовом WinAPI-шной ::WaitForSingleObject
ну или же ::WaitForMultipleObjects — здесь зависит от того, имеется одно условие выхода из цикла или несколько.

RF>Как лучше поместить эту функцию в поток--использовать класс CWinThread, воспользоваться функцией AfxBeginThread или же использовать функции WinAPI для работы с потоками?

Правильнее всего — НЕ завязываться на CWinThread (избыточен он для данной задачи), вызывать _beginthreadex либо AfxBeginThread.
ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической,
если эта функция — просто метод класса, то данное решение работать НЕ БУДЕТ!
Отредактировано 23.12.2016 5:30 AlexGin . Предыдущая версия .
Re[2]: Работа с потоками (threads) в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 26.12.16 13:08
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической,

AG>если эта функция — просто метод класса, то данное решение работать НЕ БУДЕТ!

Ясно.

И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса?
1613 г. = 2024 г.
Re[3]: Работа с потоками (threads) в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 26.12.16 13:09
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Здравствуйте, AlexGin, Вы писали:


AG>>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической,

AG>>если эта функция — просто метод класса, то данное решение работать НЕ БУДЕТ!

RF>Ясно.


RF>И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса?

Или указатель на поток (переменная типа HANDEL* или CWinThread*)?
1613 г. = 2024 г.
Re[4]: Работа с потоками (threads) в MFC
От: Evgeniy Skvortsov Россия  
Дата: 26.12.16 13:43
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>>И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса?

RF>Или указатель на поток (переменная типа HANDEL* или CWinThread*)?

Конечно может, что может этому помешать?
Re[4]: Работа с потоками (threads) в MFC
От: AlexGin Беларусь  
Дата: 27.12.16 08:15
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>>И ещё вопрос: может ли поток (переменная типа HANDLE или CWinThread) быть членом класса?

RF>Или указатель на поток (переменная типа HANDEL* или CWinThread*)?
Может, конечно же, данная переменная быть членом класса.
Я упоминал только тот факт, что головная функция рабочего потока — не бывает членом класса.
Re: Как грамотно уничтожить поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 27.12.16 11:44
Оценка:
Как я понял, для запуска потока в MFC следует воспользоваться функциями AfxBeginThread или CreateThread или _beginthreadex, для приостановки выполнения потока следует воспользоваться функцией SuspendThread, для продолжения выполнения потока--функцией ResumeThread.

А как грамотно уничтожить поток в MFC?
Мне посоветовали функцию TerminateThread, но у неё есть недостатки--после её вызов поток всё равно занимает стек памяти и имеет доступ к ядру до своего завершения.
А как грамотно уничтожить поток, чтобы у него после этого уничтожения--чтобы после этого уничтожения этот поток не выполнялся, не имел доступа к ядру и не занимал стек памяти?

Есть функции AfxEndThread и ExitThread, вызываемые внутри функции потока для его завершения. Но как правильно их использовать?
И как правильно посылать сообщения потоку и как правильно поток должен принимать и обрабатывать эти сообщения?
1613 г. = 2024 г.
Re[2]: Как грамотно уничтожить поток в MFC
От: Слава  
Дата: 27.12.16 13:01
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Есть функции AfxEndThread и ExitThread, вызываемые внутри функции потока для его завершения. Но как правильно их использовать?

RF>И как правильно посылать сообщения потоку и как правильно поток должен принимать и обрабатывать эти сообщения?

Посмотрите, как сделан дотнетовский CancellationToken и сделайте так же. В ходе вычислений поток может проверять состояние некоего volatile флага, и если он взведён, то завершать вычисление, прерывать цикл, рекурсию, и т.д. В общем случае, ничего уничтожать не надо. Поток сам завершится, когда выйдет из начальной функции, после чего его хэндл можно будет проверить чем-то вроде WaitForSingleObject с нулевым таймаутом и по коду возврата узнать, что поток завершён, после чего закрыть хэндл. Результат работы потока можно сложить в некую общую переменную, прогресс его работы можно тоже записывать в volatile переменную и периодически проверять из основного потока — по таймеру, например.
Re[3]: Как грамотно уничтожить поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 27.12.16 13:30
Оценка:
Вот у меня такая ситуация: есть функция потока MyThreadFunc, которой передаются необходимые параметры в переменной типа LPVOID. Функция MyThreadFunc переводит данные из LPVOID в структуру, из которой она передаёт данные из этой структуры в виде параметров функции MyFunc2 (эта функция не является членом класса). Функция MyFunc2 сначала создаёт объект obj класса CMyClass, а потом вызывает функцию obj.Calculate класса CMyCLass, в которой и производятся математические вычисления.

Как мне лучше в этом случае завершать выполнение потока, которому соответствует функция MyTreadFunc? Вызывать функцию TerminateThread из внешней функции (то есть оттуда, откуда был вызван этот поток), использовать AfxEndThread или ExitThread внутри функции MyThreadFunc, посылать сообщение из внешней функции этому потоку, а потом его обрабатывать, изменить код функции CMyClass::Calculate , чтобы был возможен выход из неё по получению некоего сообщения или какому-то другому признаку?
1613 г. = 2024 г.
Re[4]: Как грамотно уничтожить поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 27.12.16 13:46
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Вот у меня такая ситуация: есть функция потока MyThreadFunc, которой передаются необходимые параметры в переменной типа LPVOID.

Какой функцией запускается поток?
Re[5]: Как грамотно уничтожить поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 27.12.16 13:50
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Здравствуйте, RussianFellow, Вы писали:


RF>>Вот у меня такая ситуация: есть функция потока MyThreadFunc, которой передаются необходимые параметры в переменной типа LPVOID.

ES>Какой функцией запускается поток?

OnButton1Click
1613 г. = 2024 г.
Re[6]: Как грамотно уничтожить поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 27.12.16 13:52
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Здравствуйте, Evgeniy Skvortsov, Вы писали:


ES>>Здравствуйте, RussianFellow, Вы писали:


RF>>>Вот у меня такая ситуация: есть функция потока MyThreadFunc, которой передаются необходимые параметры в переменной типа LPVOID.

ES>>Какой функцией запускается поток?

RF>OnButton1Click


Ой, извиняюсь, неправильно понял.
Поток запускается, скажем, функцией AfxBeginThread.
1613 г. = 2024 г.
Re[6]: Как грамотно уничтожить поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 27.12.16 13:53
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>OnButton1Click


Нет, я имел ввиду какая функция используется для создания потока? AfxBeginThread, CreateThread или _beginthread ?
Re[7]: Как грамотно уничтожить поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 27.12.16 13:53
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Здравствуйте, RussianFellow, Вы писали:


RF>>OnButton1Click


ES>Нет, я имел ввиду какая функция используется для создания потока? AfxBeginThread, CreateThread или _beginthread ?


AfxBeginThread
1613 г. = 2024 г.
Re[8]: Как грамотно уничтожить поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 27.12.16 13:58
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>AfxBeginThread


Нормальное завершение потока — просто выйти из функции, то есть
return 0;
Re[9]: Как грамотно уничтожить поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 27.12.16 14:05
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Здравствуйте, RussianFellow, Вы писали:


RF>>AfxBeginThread


ES>Нормальное завершение потока — просто выйти из функции, то есть

ES>
ES>return 0;
ES>


Ясно. А как быть с функцией CMyClass::Calculation, чтобы выйти из неё по требованию извне?
Послать сообщение, а внутри этой функции устроить проверки на сообщение (вызов функции GetMessage) с проверкой значения этого сообщения или как-то иначе?

И как быть в случае, если я запускаю поток не с помощью AfxBeginthread, а с помощью CreateThread или _beginthreadex ?
1613 г. = 2024 г.
Re[10]: Как грамотно уничтожить поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 27.12.16 14:53
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Ясно. А как быть с функцией CMyClass::Calculation, чтобы выйти из неё по требованию извне?

RF>Послать сообщение, а внутри этой функции устроить проверки на сообщение (вызов функции GetMessage) с проверкой значения этого сообщения или как-то иначе?
Создать два события одно будет означать сигнал к выходу, второе — что нужно что-то посчитать

Тут есть пример как раз рабочего потока который реагирует на 2 события — нужно что-то считать и нужно завершиться.
Правда реализация ожидания из основного потока сделано криво. Он тупо засыпает в ожидании завершения расчёта. Получается никакой асинхронности.
Надо было слать какое-то асинхронное оконное сообщение из потока основному приложению.

RF>И как быть в случае, если я запускаю поток не с помощью AfxBeginthread, а с помощью CreateThread или _beginthreadex ?

То же самое — return 0.
Re[10]: Как грамотно уничтожить поток в MFC
От: Слава  
Дата: 27.12.16 14:58
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Ясно. А как быть с функцией CMyClass::Calculation, чтобы выйти из неё по требованию извне?

RF>Послать сообщение, а внутри этой функции устроить проверки на сообщение (вызов функции GetMessage) с проверкой значения этого сообщения или как-то иначе?

RF>И как быть в случае, если я запускаю поток не с помощью AfxBeginthread, а с помощью CreateThread или _beginthreadex ?


Они все в итоге вызывают CreateThread, чего бы я и вам желал. Afx... чего-то для COM в начале делает, вам это не нужно. Для завершения треда раньше времени подавайте в его параметрах — там, в структуре LPVOID некий флаг, булевскую переменную, помеченную как volatile. И в ходе вычислений проверяйте этот флаг на истинность, если истинный — прерывайте вычисления и поднимайтесь вверх до самого return 0.
Re[2]: Как грамотно уничтожить поток в MFC
От: pilgrim_ Россия  
Дата: 27.12.16 18:34
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>для приостановки выполнения потока следует воспользоваться функцией SuspendThread, для продолжения выполнения потока--функцией ResumeThread.


Можно, но не нужно. Лучше посылать потоку управляющие команды — напр. путем "посылки" (установки) соотв. событий (НЕ windows сообщений, а тех что CreateEvent), либо соотв. переменных, значение которых поток периодически будет проверять.

RF>А как грамотно уничтожить поток в MFC?


Корректно завершить работу потока — также как и не на MFC, как и в других системах/языках — сказать об этом потоку, как — см. выше.

RF>Мне посоветовали функцию TerminateThread,


Кто посоветовал? Приведи поставь сюда (c)

Не надо использовать TerminateThread.

RF>но у неё есть недостатки--после её вызов поток всё равно занимает стек памяти и имеет доступ к ядру до своего завершения.


У нее куча недостатков, не поленись, обязательно прочитай TerminateThread
И стэк потока пожалуй единственное что освобождается (был правда баг, читай по ссылке: Windows Server 2003 and Windows XP: The target thread's initial stack is not freed, causing a resource leak.), вот тебе выжимка:

TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:
If the target thread owns a critical section, the critical section will not be released.
If the target thread is allocating memory from the heap, the heap lock will not be released.
If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.


+ не будут вызваны деструкторы локальных объектов.
+ если используешь MFC AfxBeginThread — всякие дополнительные штуки, как и сам CWinThread, которые создает MFC, также не будут освобождены

RF>А как грамотно уничтожить поток, чтобы у него после этого уничтожения--чтобы после этого уничтожения этот поток не выполнялся, не имел доступа к ядру и не занимал стек памяти?


см. выше, в самом начале

RF>Есть функции AfxEndThread и ExitThread, вызываемые внутри функции потока для его завершения. Но как правильно их использовать?


Не нужно тебе их использовать, просто делай возврат (return) из функции потока, а будешь использовать см.

не будут вызваны деструкторы локальных объектов.

Re[11]: Как грамотно уничтожить поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 28.12.16 08:12
Оценка:
Здравствуйте, Слава, Вы писали:

RF>>И как быть в случае, если я запускаю поток не с помощью AfxBeginthread, а с помощью CreateThread или _beginthreadex ?


С>Они все в итоге вызывают CreateThread, чего бы я и вам желал. Afx... чего-то для COM в начале делает, вам это не нужно. Для завершения треда раньше времени подавайте в его параметрах — там, в структуре LPVOID некий флаг, булевскую переменную, помеченную как volatile. И в ходе вычислений проверяйте этот флаг на истинность, если истинный — прерывайте вычисления и поднимайтесь вверх до самого return 0.


То есть код должен иметь примерно такой вид:

typedef struct
{
  // поля структуры;
  bool  terminate;
} MyData;

DWORD WINAPI  MyThreadFunc(LPVOID lpParam)
{
  MyData* pMyData = (*MyData)lpParam;
  // действия функции;
  if (pMyData->terminate==true)
  {
    // освобождение ресурсов;
    return 0;
  }
  // действия функции;
  if (pMyData->terminate==true)
  {
    // освобождение ресурсов;
    return 0;
  }
  // действия функции;
  return 0; // завершение работы функции MyThreadFunc
}


?
1613 г. = 2024 г.
Re[12]: Как грамотно уничтожить поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 28.12.16 08:55
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>То есть код должен иметь примерно такой вид:


Нет. В таком цикле поток будет постоянно съедать процессорное время.
Надо использовать события и ожидать их в потоке через WaitForMultipleObjects

Чисто для примера:
HANDLE hEvents[2];

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    DWORD dwEvent = 0;
    bool bExit = false;
    while (!bExit)
    {
        dwEvent = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
        switch (dwEvent)
        {
        case WAIT_OBJECT_0:    
            // событие на выход
            bExit = true;
            break;
        case WAIT_OBJECT_0 + 1:    
            //событие надо считать
            // тут идут какие-то расчёты
            break;
        default:
            // что-то пошло не так, выходим
            bExit = true;
        }
    }
    return dwEvent;
}
int main()
{
    hEvents[0] = CreateEvent(NULL, FALSE, FALSE, NULL);        // событие ожидается в потоке когда поток надо завершить
    hEvents[1] = CreateEvent(NULL, FALSE, FALSE, NULL);        // событие ожидается в потоке когда надо просыпаться и что-то считать
    
    DWORD tid = 0;
    CreateThread(NULL, 0, ThreadProc, NULL, 0, &tid);        // создаем рабочий поток
    SetEvent(hEvents[1]);                        // надо считать
    return 0;
}


В MFC есть какие-то классы обёртки над всей этой многопоточностью, но я слабо разбирался с MFC реализацией. Надо читать книги по этой библиотеке.

Вообще, на мой взгляд многопоточность это не та тема, которую можно освоить за 5 минут на форуме. Нужно читать соответствующую литературу.
Отредактировано 28.12.2016 8:58 Evgeniy Skvortsov . Предыдущая версия .
Re[12]: Как грамотно уничтожить поток в MFC
От: Слава  
Дата: 28.12.16 09:08
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>То есть код должен иметь примерно такой вид:


RF>
RF>typedef struct
RF>{
RF>  // поля структуры;
RF>  bool  terminate;
RF>} MyData;
...
RF>


RF>?


Да, так. Я бы только сделал освобождение ресурсов в одном месте плюсплюсоугодно, то есть так, как его принято делать в C++ — через области видимости, деструкторы или как там еще. Создал бы еще одну функцию DoWork, в которой исполнялась бы вся работа, а ее вызов из MyThreadFunc обернул бы в try... catch, чтобы например деление на ноль не вылетело и не убило весь процесс.

Наконец, terminate я бы сделал как volatile bool terminate, чтобы она всегда читалась из памяти — где лежит, а не из какого бы то ни было кэша. Но тут спорный момент, который я не понимаю, вот обсуждения:

Здесь утверждают, что volatile в С++ работает как надо http://stackoverflow.com/questions/6995310/is-volatile-bool-for-thread-control-considered-wrong

А здесь, что в volatile является не плюсплюсоугодным и может не работать http://stackoverflow.com/questions/29633222/c-stdatomicbool-and-volatile-bool
И предлагают вместо него использовать std::atomic<bool> terminate(false)

Я такого не понимаю, если это ключевое слово языка, то зачем его ломать, а если его сломали, то почему бы тогда не выдавать ошибку компиляции при использовании сломанного? В общем, сиплюсплюс такой сиплюсплюс.
Re[13]: Как грамотно уничтожить поток в MFC
От: Слава  
Дата: 28.12.16 09:16
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Здравствуйте, RussianFellow, Вы писали:


RF>>То есть код должен иметь примерно такой вид:


ES>Нет. В таком цикле поток будет постоянно съедать процессорное время.


Вы обсуждение целиком прочитали? Где вы видите в приведенном коде какой бы то ни было бесконечный цикл? Этот тред создается для единоразового запуска расчетов, он для того и предназначен, чтобы создаться, отъесть процессорное время и помереть. А проверка флага отмены точно так же, как у RussianFellow, сделана в C# CancellationToken.

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

ES>Вообще, на мой взгляд многопоточность это не та тема, которую можно освоить за 5 минут на форуме. Нужно читать соответствующую литературу.


Ну почему же, если пару-тройку дней поэкспериментировать, то научиться вполне можно. По крайней мере, появится понимание того, о чём в литературе пишут.
Re[14]: Как грамотно уничтожить поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 28.12.16 09:54
Оценка:
Здравствуйте, Слава, Вы писали:

С>Вы обсуждение целиком прочитали? Где вы видите в приведенном коде какой бы то ни было бесконечный цикл? Этот тред создается для единоразового запуска расчетов, он для того и предназначен, чтобы создаться, отъесть процессорное время и помереть. А проверка флага отмены точно так же, как у RussianFellow, сделана в C# CancellationToken.


Сори, не внимательно прочитал.
Re[13]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 28.12.16 11:45
Оценка:
Здравствуйте, Слава, Вы писали:

С>Здравствуйте, RussianFellow, Вы писали:


RF>>То есть код должен иметь примерно такой вид:


RF>>
RF>>typedef struct
RF>>{
RF>>  // поля структуры;
RF>>  bool  terminate;
RF>>} MyData;
С>...
RF>>


RF>>?


С>Да, так.


Ясно.

У меня возник такой вопрос--а как правильно приостановить поток и как правильно запустить на продолжение приостановленный поток?
Если нежелательно использовать функции SuspendThread и ResumeThread;
Вот код:

typedef struct
{
  // поля структуры;
  int workflag; // флаг: 0--поток выполняется, 1--завершить поток, 2--приостановить поток, 3--запустить на продолжение приостановленный поток
} MyData;

DWORD WINAPI  MyThreadFunc(LPVOID  lpParam)
{
  MyData *pMyData = (MyData*)lpParam;
  // действия функции;
  if (pMyData->workflag==1)
  // завершение потока
  {
    // освобождение ресурсов;
    return 0;
  }
  if (pMyData->workflag==2)
  // приостановка потока
  {
    // освобождение ресурсов, если необходимо;
    while ((pMyData->workflag!=1)&&(pMyData->workflag!=3)); // ждём, пока не появится команда завершить поток или продолжить выполнение потока
    if (pMyData->workflag==1)
    // завершение потока
    {
      // освобождение ресурсов;
      return 0;
    }
    if (pMyData->workflag==3)
    // продолжение исполнения потока
    {
      // подключить ресурсы, если необходимо;
      workflag = 0;
    }
  }
  // действия функции;
  if (pMyData->workflag==2)
  // приостановка потока
  {
    // освобождение ресурсов, если необходимо;
    while ((pMyData->workflag!=1)&&(pMyData->workflag!=3)); // ждём, пока не появится команда завершить поток или продолжить выполнение потока
    if (pMyData->workflag==1)
    // завершение потока
    {
      // освобождение ресурсов;
      return 0;
    }
    if (pMyData->workflag==3)
    // продолжение исполнения потока
    {
      // подключить ресурсы, если необходимо;
      workflag = 0;
    }
  }
  // действия функции;
  return 0; // завершение работы функции MyThreadFunc
}


Правильно ли у меня написано? (Функция в потоке последовательно выполняет действия, а потом завершается. Никаких бесконечных циклов в ней нет).
1613 г. = 2024 г.
Re[14]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: Слава  
Дата: 28.12.16 12:10
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>У меня возник такой вопрос--а как правильно приостановить поток и как правильно запустить на продолжение приостановленный поток?

RF>Если нежелательно использовать функции SuspendThread и ResumeThread;
RF>Вот код:

RF>
RF>typedef struct
RF>{
RF>  // поля структуры;
RF>  int workflag; // флаг: 0--поток выполняется, 1--завершить поток, 2--приостановить поток, 3--запустить на продолжение приостановленный поток
RF>} MyData;
RF>


Я вам писал, что флаг должен быть помечен либо как volatile, либо std::atomic. Так как у вас написано — может работать через раз.

RF>Правильно ли у меня написано? (Функция в потоке последовательно выполняет действия, а потом завершается. Никаких бесконечных циклов в ней нет).


Моя рекомендация про один булевый флаг имеет смысл только для одной простой задачи — отмена выполнения потока. Для того, что хотите сделать вы, с приостановкой и возобновлением — лучше использовать рекомендации Скворцова, посмотрите, что он вам писал.

Но вообще, вы сами подумайте — что такое "приостановить поток"? А если он прямо сейчас из сети читает? Или делает что-то еще — захватил ресурсы, которые долго держать нельзя, а тут вы его приостановили, ресурсы держатся, а так делать нежелательно. Я не знаю точно, как работает менеджер памяти нынешнего С++ (их вообще много), но я думаю, что в многопоточном окружении он вполне может захватывать какую-нибудь блокировку при вызове new, и если SuspendThread остановит выполнение именно в это время, то после этого все остальные вызовы new будут зависать.

Обычно для остановки делаются какие-то безопасные точки, места в которых можно остановиться с гарантией того, что ничего не сломается. SuspendThread таких гарантий не даёт, вам его правильно отсоветовали.

Лично я впервые вижу такую задачу "приостановить" вычисления. Для чего это вообще надо? Зачем? Я делал подобное, но не для вычислительных задач, а для чего-то схожего с документооборотом. Зачем вам приостанавливать вычисления?
Re[15]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 28.12.16 12:57
Оценка:
Здравствуйте, Слава, Вы писали:

С>Но вообще, вы сами подумайте — что такое "приостановить поток"? А если он прямо сейчас из сети читает? Или делает что-то еще — захватил ресурсы, которые долго держать нельзя, а тут вы его приостановили, ресурсы держатся, а так делать нежелательно. Я не знаю точно, как работает менеджер памяти нынешнего С++ (их вообще много), но я думаю, что в многопоточном окружении он вполне может захватывать какую-нибудь блокировку при вызове new, и если SuspendThread остановит выполнение именно в это время, то после этого все остальные вызовы new будут зависать.


С>Лично я впервые вижу такую задачу "приостановить" вычисления. Для чего это вообще надо? Зачем? Я делал подобное, но не для вычислительных задач, а для чего-то схожего с документооборотом. Зачем вам приостанавливать вычисления?


Ну, например, делаются какие-то вычисления. Результаты выводятся в консольное окно (никакие другие ресурсы при этом не захватываются). Это консольное окно находится на заднем плане экрана--а на переднем плане находится диалоговое окно приложения. И если пользователь хочет посмотреть процесс вычислений, то при нажатии на кнопку диалогового окна на передний план экрана помещается консольное окно, в которое выводятся результаты вычислений--пользователь видит, что было вычислено к текущему моменту. А потом, при нажатии на другую кнопку диалогового окна, это диалоговое окно опять помещается на передний план экрана, а процесс вычислений продолжается.
1613 г. = 2024 г.
Re[15]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 28.12.16 13:14
Оценка:
Здравствуйте, Слава, Вы писали:

С>Моя рекомендация про один булевый флаг имеет смысл только для одной простой задачи — отмена выполнения потока. Для того, что хотите сделать вы, с приостановкой и возобновлением — лучше использовать рекомендации Скворцова, посмотрите, что он вам писал.


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

Мне кажется, что такая конструкция допустима:

if (pMyData->workflag==2)
// приостановка потока
{
  // освобождение ресурсов, если необходимо;
  while ((pMyData->workflag!=1)&&(pMyData->workflag!=3)); // ждём, пока не появится команда завершить поток или продолжить выполнение потока
  if (pMyData->workflag==1)
  // завершение потока
  {
    // освобождение ресурсов;
    return 0;
  }
  if (pMyData->workflag==3)
  // продолжение исполнения потока
  {
    // подключить ресурсы, если необходимо;
    workflag = 0;
  }
}


В крайнем случае, её можно немного изменить:

if (pMyData->workflag==2)
// приостановка потока
{
  // освобождение ресурсов, если необходимо;
  while ((pMyData->workflag!=1)&&(pMyData->workflag!=3))
  // ждём, пока не появится команда завершить поток или продолжить выполнение потока
  {
    Sleep(1000); // задержка выполнения потока на одну секунду, в функции Sleep можно поставить любое другое число
  }
  if (pMyData->workflag==1)
  // завершение потока
  {
    // освобождение ресурсов;
    return 0;
  }
  if (pMyData->workflag==3)
  // продолжение исполнения потока
  {
    // подключить ресурсы, если необходимо;
    workflag = 0;
  }
}
1613 г. = 2024 г.
Re[2]: Работа с потоками (threads) в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 28.12.16 14:17
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>Правильнее всего — НЕ завязываться на CWinThread (избыточен он для данной задачи), вызывать _beginthreadex либо AfxBeginThread.

AG>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической

То есть если я функцию, которая производит вычисления, помечу как static, то её можно сделать потоковой? И есть ли в этом случае какие-то особенности по работе с такой функцией по сравнению с глобальными потоковыми функциями?
1613 г. = 2024 г.
Re[16]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 28.12.16 20:44
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Евгений мне написал про другой случай--когда есть цикл (вообще говоря, бесконечный)

Неверно! цикл не бесконечный, он управляется основным потоком, который породил этот поток вычислений. Там можно ожидать хоть 20 событий, зависящих от внешних условий. И только основной поток знает как командовать своими "детьми".
В моём примитивном примере показан общий ход мысли. Вообще настоящий промышленный код будет выглядеть раза в 3 сложнее. Проверка кодов возврата и т.д.
Вообще мне странно писать всё это. По сути я печатаю тут свой авторский перевод справки MSDN, где описано ровно то же, и ссылки на примеры кода с пояснениями я привожу в каждом ответе.
Я уже не раз писал, что практически вся исчерпывающая информация есть в MSDN. Лично я так и научился, читая MSDN, пробуя, ошибаясь, думая, временами задавая вопросы на форумах ...
Всё это наводит на мысль, что твой английский настолько плох, что даже не позволяет читать MSDN. И это печально. Я с трудом шпрехаю на инглише, но тех документы читаю достаточно свободно.

Твои же попытки выглядят как у студента горит лаба, учить некогда, заспамлю весь инет своими вопросами. Это я к тому, что ты задаёшь свои вопросы одновременно на нескольких разных сайтах.

Я ещё раз повторю — многопоточность это не самая простая тема, в неё нужно вникать и читать авторитетных людей.

В связи с этим про многопоточность я перестаю отвечать, по причине не понимания тобой базовых принципов.
Без обид.

P.S. Тебе накидали рейтинга и сейчас ты можешь писать в политику. Не видел ни одного сообщения пока, несмотря на заявления.
Re[16]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: Evgeniy Skvortsov Россия  
Дата: 28.12.16 20:53
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Ну, например, делаются какие-то вычисления. Результаты выводятся в консольное окно (никакие другие ресурсы при этом не захватываются). Это консольное окно находится на заднем плане экрана--а на переднем плане находится диалоговое окно приложения. И если пользователь хочет посмотреть процесс вычислений, то при нажатии на кнопку диалогового окна на передний план экрана помещается консольное окно, в которое выводятся результаты вычислений--пользователь видит, что было вычислено к текущему моменту. А потом, при нажатии на другую кнопку диалогового окна, это диалоговое окно опять помещается на передний план экрана, а процесс вычислений продолжается.


Это классический пример неверного проектирования логики ПО.
Если есть два окна, пользователь может сам ткнуть мышью и вызвать окно на первйй план.
По поводу принудительного всплывания недавно была тема в WINAPI, там всё разложили по полочкам.

В общем, нужно учить матчасть.
Re[17]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 29.12.16 06:10
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Я уже не раз писал, что практически вся исчерпывающая информация есть в MSDN. Лично я так и научился, читая MSDN, пробуя, ошибаясь, думая, временами задавая вопросы на форумах ...

ES>Всё это наводит на мысль, что твой английский настолько плох, что даже не позволяет читать MSDN. И это печально. Я с трудом шпрехаю на инглише, но тех документы читаю достаточно свободно.

Мой английский неплохой. Просто MSDN--это одно, а объяснение того, что написано в MSDN--это другое. И примеры кода не всегда на MSDN есть.

ES>Твои же попытки выглядят как у студента горит лаба, учить некогда, заспамлю весь инет своими вопросами. Это я к тому, что ты задаёшь свои вопросы одновременно на нескольких разных сайтах.


Ну да, правильно, времени у меня мало.

ES>Я ещё раз повторю — многопоточность это не самая простая тема, в неё нужно вникать и читать авторитетных людей.


ES>В связи с этим про многопоточность я перестаю отвечать, по причине не понимания тобой базовых принципов.

ES>Без обид.

ES>P.S. Тебе накидали рейтинга и сейчас ты можешь писать в политику. Не видел ни одного сообщения пока, несмотря на заявления.


Что же, спасибо. Боюсь, что меня забанят в разделе "Политика". Или за нарушение правил форума (например, "разжигание межнациональной ненависти"), или просто за то, что модераторам не понравится то, что я пишу.
1613 г. = 2024 г.
Re[18]: Как грамотно приостановить и запустить на продолжение поток в MFC
От: aloch Россия  
Дата: 29.12.16 23:01
Оценка: +1
Здравствуйте, RussianFellow, Вы писали:

RF>Здравствуйте, Evgeniy Skvortsov, Вы писали:


RF>Мой английский неплохой. Просто MSDN--это одно, а объяснение того, что написано в MSDN--это другое. И примеры кода не всегда на MSDN есть.


Тебе уже сказали — почитай Рихтера прежде чем лезть к потокам и писать глючный софт. http://wm-help.net/books-online/book/59464.html 0 — там главы 6-11. Уж сто раз бы прочитал, чем спрашивать на форуме.


Re[3]: Работа с потоками (threads) в MFC
От: Evgeniy Skvortsov Россия  
Дата: 30.12.16 09:39
Оценка: 6 (1)
Здравствуйте, RussianFellow, Вы писали:

RF> То есть если я функцию, которая производит вычисления, помечу как static, то её можно сделать потоковой?

Да

RF> И есть ли в этом случае какие-то особенности по работе с такой функцией по сравнению с глобальными потоковыми функциями?


Функция член класса с модификатором static ни чем не отличается от глобальной, кроме того что находится в пространстве имени класса.

Все обычные функции члены класса вызываются со скрытым указателем на экземпляр класса
То есть если есть класс
struct A {
  int m_a;
  void f() {m_a++;}
  static DWORD WINAPI MyThreadFunc(LPVOID lpParam) {return 0;}
};

То при вызове функции f(), на самом деле вызов идёт как f(this) и поэтому внутри функции можно обращаться к переменным, в частности к m_a.
А вот внутри MyThreadFunc — обращаться к m_a уже нельзя. Так как статические функции вызываются так, как они объявлены, this не передаётся.
Но ей передаётся lpParam, поэтому при создании потока можно передать this явно
AfxBeginThread(MyThreadFunc, this)


А в функции потока использовать указатель на экземпляр класса
DWORD WINAPI MyThreadFunc(LPVOID lpParam) {
  A *a = static_cast<A*>(lpParam);
  а-> вызываем что угодно
  return 0;
}


Можно сделать из MyThreadFunc функцию переходник, которая будет звать через указатель нормальную функцию член, в которой можно спокойно работать с данными класса
struct A {
  int m_a;
  void f() {m_a++;}
  static DWORD WINAPI MyThreadFunc(LPVOID lpParam);
  DWORD RealThreadFunc(){};
};
DWORD WINAPI A::MyThreadFunc(LPVOID lpParam) {
  A *a = static_cast<A*>(lpParam);
  return а->RealThreadFunc();
}


Обрати внимание, статические функции должны определяться с указанием имени класса.
Вкратце так.
Отредактировано 30.12.2016 11:24 Evgeniy Skvortsov . Предыдущая версия .
Re[3]: Работа с потоками (threads) в MFC
От: AlexGin Беларусь  
Дата: 30.12.16 09:46
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Здравствуйте, AlexGin, Вы писали:


AG>>Правильнее всего — НЕ завязываться на CWinThread (избыточен он для данной задачи), вызывать _beginthreadex либо AfxBeginThread.

AG>>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической

RF>То есть если я функцию, которая производит вычисления, помечу как static, то её можно сделать потоковой? И есть ли в этом случае какие-то особенности по работе с такой функцией по сравнению с глобальными потоковыми функциями?


Пометить как "static" — возможно вызовет ошибку компиляции, т.к. не должно быть прямого обращения к мемберам класса внутри.
Глобальные функции, также как и статические — НЕ ЯВЛЯЮТСЯ ЧЛЕНАМИ КЛАССА.
Все особенности определяются именно данным моментом.

P.S. Здесь чуть выше, товарищ Евгений Скворцов — всё очень толково объяснил!
P.P.S. Книга Джеффри Рихтера: "Создание эффетивных Win-32 приложений..." — это то, что Вам, RussianFellow, нужно!
Отредактировано 30.12.2016 9:51 AlexGin . Предыдущая версия .
Re[4]: Работа с потоками (threads) в MFC
От: pilgrim_ Россия  
Дата: 30.12.16 10:09
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>Глобальные функции, также как и статические — НЕ ЯВЛЯЮТСЯ ЧЛЕНАМИ КЛАССА.


Не вноси хауос в мозг ТС , конечно же статические методы класса являются его членами, их так и называют: static member_function , но не связанными с экземплярами (объектами) класса.
Отредактировано 30.12.2016 10:31 pilgrim_ . Предыдущая версия .
Re[5]: Работа с потоками (threads) в MFC
От: Слава  
Дата: 30.12.16 10:27
Оценка: :))
Здравствуйте, pilgrim_, Вы писали:

_>Не вноси хаус в мозг ТС

_>хаус
_>хаус

Уместная цитата про рихтера из "Даун хауса"
https://www.youtube.com/watch?v=j2hZgIIaIyI

Давайте посоветуем топикстартеру использовать IOCP. Через него тоже можно таски по потокам раскидывать.
Re[3]: Работа с потоками (threads) в MFC
От: MasterZiv СССР  
Дата: 30.12.16 14:32
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Здравствуйте, _Butch_, Вы писали:


_B_>>std::thread


RF>А если функция, которую я хочу поместить в поток, является членом класса--то как быть?



std::thread это умеет.
Re: Вопросы по функциям _beginthreadex и _endthreadex
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 11.01.17 12:12
Оценка:
Я прочитал главы 6--11 книги Джеффри Рихтера "WINDOWS. Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows" :

http://wm-help.net/books-online/book/59464.html

И у меня возникли два вопроса:

1) Когда вызываем функцию _beginthreadex, то после завершения работы потока (в результате использования функций WaitForSingleObject или WaitForMultipleObjects) обязательно ли следует закрывать дескриптор потока hThread (который был создан функцией _beginthreadex) с помощью функции CloseHandle(hThread); ?

2) Внутри потоковой функции обязательно ли использовать функцию _endthreadex для завершения работы потока или же можно завершить этот поток с помощью return в потоковой функции?

Как я понял, для запуска работы потока лучше всего использовать функцию _beginthreadex, чем функции CreateThread или AfxBeginThread.
1613 г. = 2024 г.
Re[4]: Работа с потоками (threads) в MFC
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 11.01.17 12:18
Оценка:
Здравствуйте, AlexGin, Вы писали:

RF>>То есть если я функцию, которая производит вычисления, помечу как static, то её можно сделать потоковой? И есть ли в этом случае какие-то особенности по работе с такой функцией по сравнению с глобальными потоковыми функциями?

AG>
AG>Пометить как "static" — возможно вызовет ошибку компиляции, т.к. не должно быть прямого обращения к мемберам класса внутри.

А что, разве внутри какого-то класса его статическая функция не может обращаться к нестатическим членам (функциям и данным) этого класса?
Вся разница лишь в том, что нестатические функции и данные создаются для каждого объекта класса, а статические функции и данные класса существуют в единственном экземляре--они относятся не к объектам класса, а к самому классу.
1613 г. = 2024 г.
Re[2]: Вопросы по функциям _beginthreadex и _endthreadex
От: Evgeniy Skvortsov Россия  
Дата: 11.01.17 13:34
Оценка: 4 (1)
Здравствуйте, RussianFellow, Вы писали:


RF>1) Когда вызываем функцию _beginthreadex, то после завершения работы потока (в результате использования функций WaitForSingleObject или WaitForMultipleObjects) обязательно ли следует закрывать дескриптор потока hThread (который был создан функцией _beginthreadex) с помощью функции CloseHandle(hThread); ?


Если поток создан при помощи _beginthread и выход из потока осуществляется посредством вызова функции _endthread, то не нужно (она сама закрывает хэндл).
Если поток создаётся с использованием AfxBeginThread, то же не нужно. Там внутри поток создаётся не напрямую, а создается экземпляр класса CWinThread, в деструкторе которого вызывается CloseHandle.

Во всех остальных случаях нужно вызвать CloseHandle, иначе возникает утечка ресурсов.

RF>2) Внутри потоковой функции обязательно ли использовать функцию _endthreadex для завершения работы потока или же можно завершить этот поток с помощью return в потоковой функции?

ExitThread is the preferred method of exiting a thread in C code. However, in C++ code, the thread is exited before any destructors can be called or any other automatic cleanup can be performed. Therefore, in C++ code, you should return from your thread function.

Понятно о чём речь? Кратко — в программах на с++ надо использовать return.

RF>Как я понял, для запуска работы потока лучше всего использовать функцию _beginthreadex, чем функции CreateThread или AfxBeginThread.


_beginthread или _beginthreadex надо использовать если в потоке вызываются какие-то функции CRT.

А вообще Рихтер настоятельно рекомендует использовать _beginthreadex. Что в общем-то логично.
В целом, что бы не забивать голову лишней информацией и избежать всех возможных проблем — правильно использовать _beginthreadex для создания потока и return 0 для выхода из него. С последующим CloseHandle
Отредактировано 11.01.2017 14:38 Evgeniy Skvortsov . Предыдущая версия . Еще …
Отредактировано 11.01.2017 14:28 Evgeniy Skvortsov . Предыдущая версия .
Отредактировано 11.01.2017 14:11 Evgeniy Skvortsov . Предыдущая версия .
Отредактировано 11.01.2017 14:10 Evgeniy Skvortsov . Предыдущая версия .
Re[3]: Вопросы по функциям _beginthreadex и _endthreadex
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 11.01.17 14:44
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>А вообще Рихтер настоятельно рекомендует использовать _beginthreadex. Что в общем-то логично.

ES>В целом, что бы не забивать голову лишней информацией и избежать всех возможных проблем — правильно использовать _beginthreadex для создания потока и return 0 для выхода из него. С последующим CloseHandle

Ясно, понял. Спасибо!
1613 г. = 2024 г.
Re: Проблема с потоком, работающим с компонентами диалогового окна
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 12.01.17 10:11
Оценка:
Есть у меня диалог IDD_CALCMANDIALOG, которому соответствует класс CCalcManDialog.
На этом диалоге расположены кнопки "Остановить", "Продолжить", "Выход" и компонент IDC_STATIC1, которому соответствует переменная m_IDC_STATIC1 типа CStatic.
При открытии этого диалога происходят некоторые действия, которые я поместил в потоковые функции.

Первый вариант--здесь в потоке выводятся в консольное окно числа от 1 до 20000 :

typedef struct
{
  volatile int flag;
} MyStruct;

class CCalcManDialog : public CDialogEx
{
  // данные и методы класса CCalcManDialog;
  MyStruct  myStruct;
  unsigned  threadID;
  HANDLE hThread;
  // методы и данные класса CCalcManDialog;
};

unsigned int __stdcall MyThreadFunc(void* pParam)
{
  int  i;
  MyStruct  *pMyStruct = (MyStruct*)pParam;
  volatile int  *pflag = &(pMyStruct->flag);

  for (i=0; i<20000; i++)
  {
    printf("i = %d\n",i);
    if (*pflag==0)
    {
      printf("The thread was terminated.\n"); // поток был принудительно завершён
      return 0;
    }
    if (*pflag==2) // поток был приостановлен
    {
        while ((*pflag!=0)&&(*pflag!=1))
            Sleep(1000);
        if (*pflag==0)
        {
            printf("The thread was terminated.\n"); // поток был принудительно завершён (после своей приостановки)
            return 0;
        }
    }
  }
  printf("The thread was finished successfully.\n"); // поток завершился благополучно (естественным образом)
  return 0;
}

BOOL CCalcManDialog::OnInitDialog()
{
    CDialog::OnInitDIalog();
    myStruct.flag = 1;
    hThread = (HANDLE)_beginthreadex(NULL,0,&MyThreadFunc,&myStruct,0,&threadID);
    return TRUE;
}

void CCalcManDialog::OnBnClickedOk()
// если была нажата клавиша "Выйти"
{
    myStruct.flag = 0;
    WaitForSingleObject(hThread,INFINITE);
    CloseHandle(hThread);
    CDialogEx::OnOk();
}


В этом варианте всё работает нормально--при открытии диалога в диалоговое окно выводятся целочисленные значения ( *pflag==1 ), при нажатии на кнопку "Остановить" ( *pflag==2 ) поток приостанавливает свою работу и ждёт либо своего возобновления ( кнопка "Продолжить", *pflag==1 ), либо принудительного завершения ( кнопка "Выйти", *pflag==0 ). Если поток работает и до его завершения была нажата кнопка "Выйти" ( *pflag==0 ), то поток принудительно завершается до своего окончания работы. Если же никаких из этих кнопок не было нажато, то поток выполняется до своего завершения.

Второй вариант--здесь в потоке выводится в окно GUI-диалога, в его компонент IDC_STATIC1 (которому соответствует переменная m_IDC_STATIC1) в цикле сначала надпись "Расчёт выполняется", затем через секунду надпись "Расчёт выполняется.", потом через секунду надпись "Расчёт выполняется . .", после этого через секунду надпись "Расчёт выполняется . . .", далее через секунду надпись "Расчёт выполняется" и т.д.:

class  CCalcManDialog;

typedef struct
{
  CCalcManDialog *pCalManDialog;
  volatile int flag2;
} MyStruct2;

class CCalcManDialog : public CDialogEx
{
  // данные и методы класса CCalcManDialog;
  MyStruct2  myStruct2;
  unsigned  threadID2;
  HANDLE  hThread2;
  void  setTextIntoSTatic(char *s);
  CStatic  m_IDC_STATIC1;
  // данные и методы класса CCalcManDialog;
};

unsigned int __stdcall MyThreadFunc2(void* pParam)
{
  int  i;
  char s[50];
  bool localflag = true;
  MyStruct2 *pMyStruct2 = (MyStruct2*)pParam;
  CCalcManDialog *pCalcManDialog = pMyStruct->pCalcManDialog;
  volatile int *pflag2 = &(pmyStruct->flag2);

  while (localflag==true)
  {
    sprintf(s,"Расчёт выполняется");
    pCalcManDialog->setTextIntoStatic(s);
    Sleep(1000);
    sprintf(s,"Расчёт выполняется .");
    Sleep(1000);
    sprintf(s,"Расчёт выполняется . .");
    Sleep(1000);
    sprintf(s,"Расчёт выполняется . . .");
    Sleep(1000);
    if (*pflag2==0)
    {
        sprintf(s,"Расчёт выполнен");
        pCalcManDialog->setTextIntoStatic(s);
        localflag = false;
        break;
    }
    if (*pflag2==2)
    {
        while ((*pflag2!=0)&&(*pflag2!=1))
            Sleep(1000);
        if (*pflag2==0)
        {
            sprintf(s,"Расчёт выполнен");
            pCalcManDialog->setTextIntoStatic(s);
            localflag = false;
            break;
        }
    }
  }
  return 0;
}

void  CCalcManDialog::setTextIntoStatic(char *s)
{
    m_IDC_STATIC1.SetWindowsTextA(s);
}

BOOL  CCalcManDialog::OnInitDialog()
{
    CDialog::OnInitDialog();
    myStruct2.flag2 = 1;
    myStruct2.pCalcManDialog = this;
    hThread2 = (HANDLE)_beginthreadex(NULL,0,&MyThreadFunc2,&myStruct2,0,&threadID2);
    return TRUE;
}

void  CCalcManDialog::OnBnClickedOk()
{
    myStruct2.flag2 = 0;
    WaitForSingleObject(hThread2,INFINITE);
    CloseHandle(hThread);
    CDialogEx::OnOk();
}


В этом случае у меня запускается поток выводящий бегущую строку (надписи "Расчёт выполняется", "Расчёт выполняется .", "Расчёт выполняется . .", "Расчёт выполняется . . ."), но при нажатии на кнопку "Выйти" в функции CCalcManDialog::OnBnClickedOk на строке WaitForSingleObject(hThread2,INFINITE); у меня программа зависает (ждёт бесконечно долго, хотя я и поставил флаг myStruct2.flag2 равным 0). Если же в этой функции заменить WaitForSingleObject(hThread2,INFINITE); на WaitForSingleObject(hThread2,2000); то программа ждёт две секунды, переходит потом на CloseHandle(hThread); и далее--то есть поток завершается. Но по выходе из функции CCalcManDialog::OnBnClickedOk у меня отладчик проваливается в функцию MyThreadFunc2, где он бьётся в одном из мест, где я проверяю значение flag2 (то есть myStruct2.flag2) на 0.

Как исправить эту ошибку? Как сделать так, чтобы и при работе с GUI-диалогом (его компонентами) мои потоковые функции нормально работали, то есть чтобы происходило нормальное завершение их работы по флагу?
Может быть, как-то по-другому следует вызывать функцию _beginthreadex или же вместо неё следует пользоваться функцией AfxBeginThread или классом CWinThread ?
1613 г. = 2024 г.
Re[2]: Проблема с потоком, работающим с компонентами диалогового окна
От: Evgeniy Skvortsov Россия  
Дата: 12.01.17 11:45
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Как исправить эту ошибку? Как сделать так, чтобы и при работе с GUI-диалогом (его компонентами) мои потоковые функции нормально работали, то есть чтобы происходило нормальное завершение их работы по флагу?


Я так понял ты используешь поток для вывода бегущей строки? Это перебор.
Вообще лезть в оконные дела из другого потока — это не есть гуд и чревато непредсказуемыми последствиями и в частности дедлоками.

В твоём случае бегущую строку лучше реализовать через таймер. То есть запускаешь таймер SetTimer(1000) и обрабатываешь сообщение WM_TIMER. В обработчике уже выводишь текст как нужно. Система сама будет вызывать обработчик через указанный интервал. Когда нужно прекратить вызов таймера — останавливаешь его с помощью KillTimer.

RF>Может быть, как-то по-другому следует вызывать функцию _beginthreadex или же вместо неё следует пользоваться функцией AfxBeginThread или классом CWinThread ?

Это здесь не при чём.
Re[3]: Проблема с потоком, работающим с компонентами диалогового окна
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 12.01.17 13:58
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>В твоём случае бегущую строку лучше реализовать через таймер. То есть запускаешь таймер SetTimer(1000) и обрабатываешь сообщение WM_TIMER. В обработчике уже выводишь текст как нужно. Система сама будет вызывать обработчик через указанный интервал. Когда нужно прекратить вызов таймера — останавливаешь его с помощью KillTimer.


Ясно. Для того, чтобы запустить таймер, нужно воспользоваться функцией SetTimer, а для того, чтобы выключить таймер, нужно воспользоваться функцией KillTimer:

https://msdn.microsoft.com/ru-ru/library/49313fdf.aspx

https://msdn.microsoft.com/ru-ru/library/sfs37s7c.aspx

http://www.firststeps.ru/mfc/steps/r.php?32

Это у меня работает.

Но как правильно поступить с таймером, если запустил его один раз, потом пользователь нажал на кнопку "Приостановить" (таймер должен остановить свою работу), затем пользователь нажимает на кнопку "Продолжить" (таймер должен заработать заново), а потом пользователь нажимает на кнопку "Выйти" (таймер должен прекратить свою работу)? Как это правильно организовать?
1613 г. = 2024 г.
Re[4]: Проблема с потоком, работающим с компонентами диалогового окна
От: Evgeniy Skvortsov Россия  
Дата: 12.01.17 14:48
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Но как правильно поступить с таймером, если запустил его один раз, потом пользователь нажал на кнопку "Приостановить" (таймер должен остановить свою работу), затем пользователь нажимает на кнопку "Продолжить" (таймер должен заработать заново), а потом пользователь нажимает на кнопку "Выйти" (таймер должен прекратить свою работу)? Как это правильно организовать?


По кнопке "Приостановить" — KillTimer, по кнопке "Продолжить" — SetTimer, по кнопке "Выйти" — KillTimer
Re[5]: Проблема с потоком, работающим с компонентами диалогового окна
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 13.01.17 06:53
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Здравствуйте, RussianFellow, Вы писали:


RF>>Но как правильно поступить с таймером, если запустил его один раз, потом пользователь нажал на кнопку "Приостановить" (таймер должен остановить свою работу), затем пользователь нажимает на кнопку "Продолжить" (таймер должен заработать заново), а потом пользователь нажимает на кнопку "Выйти" (таймер должен прекратить свою работу)? Как это правильно организовать?


ES>По кнопке "Приостановить" — KillTimer, по кнопке "Продолжить" — SetTimer, по кнопке "Выйти" — KillTimer


Ясно, спасибо.
1613 г. = 2024 г.
Re: Работа с потоками (threads) в MFC
От: MasterZiv СССР  
Дата: 26.12.17 14:43
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Не подскажете ли, уважаемые коллеги, как лучше всего работать с потоками (threads) в MFC?

RF>Есть функция, производящая математические вычисления. Эта функция большая по длине и её действия происходят в цикле до выполнения некоего условия. Эта функция работает в консольном режиме--ввод и вывод данных там происходит с помощью консольного окна.
RF>Как лучше поместить эту функцию в поток--использовать класс CWinThread, воспользоваться функцией AfxBeginThread или же использовать функции WinAPI для работы с потоками?

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

AfxBeginThread помимо всего прочего инициализирует per-thread структуры данных MFC, поэтому создавать поток, из которого будет использоваться MFC, нужно именно через эту функцию (или аналог).
Re[3]: Работа с потоками (threads) в MFC
От: MasterZiv СССР  
Дата: 26.12.17 14:44
Оценка:
Здравствуйте, RussianFellow, Вы писали:


RF>А если функция, которую я хочу поместить в поток, является членом класса--то как быть?


Статическим членом она может быть, при этом это никак не влияет на твой выбор.
Нестатическим членом она быть не может, потому как её сигнатура не подойдёт для функции потока.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.