Здравствуйте, AlexGin, Вы писали:
AG>Правильнее всего — НЕ завязываться на CWinThread (избыточен он для данной задачи), вызывать _beginthreadex либо AfxBeginThread. AG>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической
То есть если я функцию, которая производит вычисления, помечу как static, то её можно сделать потоковой? И есть ли в этом случае какие-то особенности по работе с такой функцией по сравнению с глобальными потоковыми функциями?
1613 г. = 2024 г.
Re[16]: Как грамотно приостановить и запустить на продолжение поток в MFC
Здравствуйте, RussianFellow, Вы писали:
RF>Евгений мне написал про другой случай--когда есть цикл (вообще говоря, бесконечный)
Неверно! цикл не бесконечный, он управляется основным потоком, который породил этот поток вычислений. Там можно ожидать хоть 20 событий, зависящих от внешних условий. И только основной поток знает как командовать своими "детьми".
В моём примитивном примере показан общий ход мысли. Вообще настоящий промышленный код будет выглядеть раза в 3 сложнее. Проверка кодов возврата и т.д.
Вообще мне странно писать всё это. По сути я печатаю тут свой авторский перевод справки MSDN, где описано ровно то же, и ссылки на примеры кода с пояснениями я привожу в каждом ответе.
Я уже не раз писал, что практически вся исчерпывающая информация есть в MSDN. Лично я так и научился, читая MSDN, пробуя, ошибаясь, думая, временами задавая вопросы на форумах ...
Всё это наводит на мысль, что твой английский настолько плох, что даже не позволяет читать MSDN. И это печально. Я с трудом шпрехаю на инглише, но тех документы читаю достаточно свободно.
Твои же попытки выглядят как у студента горит лаба, учить некогда, заспамлю весь инет своими вопросами. Это я к тому, что ты задаёшь свои вопросы одновременно на нескольких разных сайтах.
Я ещё раз повторю — многопоточность это не самая простая тема, в неё нужно вникать и читать авторитетных людей.
В связи с этим про многопоточность я перестаю отвечать, по причине не понимания тобой базовых принципов.
Без обид.
P.S. Тебе накидали рейтинга и сейчас ты можешь писать в политику. Не видел ни одного сообщения пока, несмотря на заявления.
Re[16]: Как грамотно приостановить и запустить на продолжение поток в MFC
Здравствуйте, RussianFellow, Вы писали:
RF>Ну, например, делаются какие-то вычисления. Результаты выводятся в консольное окно (никакие другие ресурсы при этом не захватываются). Это консольное окно находится на заднем плане экрана--а на переднем плане находится диалоговое окно приложения. И если пользователь хочет посмотреть процесс вычислений, то при нажатии на кнопку диалогового окна на передний план экрана помещается консольное окно, в которое выводятся результаты вычислений--пользователь видит, что было вычислено к текущему моменту. А потом, при нажатии на другую кнопку диалогового окна, это диалоговое окно опять помещается на передний план экрана, а процесс вычислений продолжается.
Это классический пример неверного проектирования логики ПО.
Если есть два окна, пользователь может сам ткнуть мышью и вызвать окно на первйй план.
По поводу принудительного всплывания недавно была тема в WINAPI, там всё разложили по полочкам.
В общем, нужно учить матчасть.
Re[17]: Как грамотно приостановить и запустить на продолжение поток в MFC
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>Я уже не раз писал, что практически вся исчерпывающая информация есть в MSDN. Лично я так и научился, читая MSDN, пробуя, ошибаясь, думая, временами задавая вопросы на форумах ... ES>Всё это наводит на мысль, что твой английский настолько плох, что даже не позволяет читать MSDN. И это печально. Я с трудом шпрехаю на инглише, но тех документы читаю достаточно свободно.
Мой английский неплохой. Просто MSDN--это одно, а объяснение того, что написано в MSDN--это другое. И примеры кода не всегда на MSDN есть.
ES>Твои же попытки выглядят как у студента горит лаба, учить некогда, заспамлю весь инет своими вопросами. Это я к тому, что ты задаёшь свои вопросы одновременно на нескольких разных сайтах.
Ну да, правильно, времени у меня мало.
ES>Я ещё раз повторю — многопоточность это не самая простая тема, в неё нужно вникать и читать авторитетных людей.
ES>В связи с этим про многопоточность я перестаю отвечать, по причине не понимания тобой базовых принципов. ES>Без обид.
ES>P.S. Тебе накидали рейтинга и сейчас ты можешь писать в политику. Не видел ни одного сообщения пока, несмотря на заявления.
Что же, спасибо. Боюсь, что меня забанят в разделе "Политика". Или за нарушение правил форума (например, "разжигание межнациональной ненависти"), или просто за то, что модераторам не понравится то, что я пишу.
1613 г. = 2024 г.
Re[18]: Как грамотно приостановить и запустить на продолжение поток в MFC
Здравствуйте, RussianFellow, Вы писали:
RF>Здравствуйте, Evgeniy Skvortsov, Вы писали:
RF>Мой английский неплохой. Просто MSDN--это одно, а объяснение того, что написано в MSDN--это другое. И примеры кода не всегда на MSDN есть.
Тебе уже сказали — почитай Рихтера прежде чем лезть к потокам и писать глючный софт. http://wm-help.net/books-online/book/59464.html 0 — там главы 6-11. Уж сто раз бы прочитал, чем спрашивать на форуме.
Здравствуйте, 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();
}
Обрати внимание, статические функции должны определяться с указанием имени класса.
Вкратце так.
Здравствуйте, RussianFellow, Вы писали:
RF>Здравствуйте, AlexGin, Вы писали:
AG>>Правильнее всего — НЕ завязываться на CWinThread (избыточен он для данной задачи), вызывать _beginthreadex либо AfxBeginThread. AG>>ВАЖНО: и в том и в другом случае "функция, производящая математические вычисления" должна быть глобальной или же статической
RF>То есть если я функцию, которая производит вычисления, помечу как static, то её можно сделать потоковой? И есть ли в этом случае какие-то особенности по работе с такой функцией по сравнению с глобальными потоковыми функциями?
Пометить как "static" — возможно вызовет ошибку компиляции, т.к. не должно быть прямого обращения к мемберам класса внутри.
Глобальные функции, также как и статические — НЕ ЯВЛЯЮТСЯ ЧЛЕНАМИ КЛАССА.
Все особенности определяются именно данным моментом.
P.S. Здесь чуть выше, товарищ Евгений Скворцов — всё очень толково объяснил!
P.P.S. Книга Джеффри Рихтера: "Создание эффетивных Win-32 приложений..." — это то, что Вам, RussianFellow, нужно!
Здравствуйте, AlexGin, Вы писали:
AG>Глобальные функции, также как и статические — НЕ ЯВЛЯЮТСЯ ЧЛЕНАМИ КЛАССА.
Не вноси хауос в мозг ТС , конечно же статические методы класса являются его членами, их так и называют: static member_function , но не связанными с экземплярами (объектами) класса.
Здравствуйте, RussianFellow, Вы писали:
RF>Здравствуйте, _Butch_, Вы писали:
_B_>>std::thread
RF>А если функция, которую я хочу поместить в поток, является членом класса--то как быть?
std::thread это умеет.
Re: Вопросы по функциям _beginthreadex и _endthreadex
1) Когда вызываем функцию _beginthreadex, то после завершения работы потока (в результате использования функций WaitForSingleObject или WaitForMultipleObjects) обязательно ли следует закрывать дескриптор потока hThread (который был создан функцией _beginthreadex) с помощью функции CloseHandle(hThread); ?
2) Внутри потоковой функции обязательно ли использовать функцию _endthreadex для завершения работы потока или же можно завершить этот поток с помощью return в потоковой функции?
Как я понял, для запуска работы потока лучше всего использовать функцию _beginthreadex, чем функции CreateThread или AfxBeginThread.
Здравствуйте, AlexGin, Вы писали:
RF>>То есть если я функцию, которая производит вычисления, помечу как static, то её можно сделать потоковой? И есть ли в этом случае какие-то особенности по работе с такой функцией по сравнению с глобальными потоковыми функциями? AG> AG>Пометить как "static" — возможно вызовет ошибку компиляции, т.к. не должно быть прямого обращения к мемберам класса внутри.
А что, разве внутри какого-то класса его статическая функция не может обращаться к нестатическим членам (функциям и данным) этого класса?
Вся разница лишь в том, что нестатические функции и данные создаются для каждого объекта класса, а статические функции и данные класса существуют в единственном экземляре--они относятся не к объектам класса, а к самому классу.
1613 г. = 2024 г.
Re[2]: Вопросы по функциям _beginthreadex и _endthreadex
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
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>А вообще Рихтер настоятельно рекомендует использовать _beginthreadex. Что в общем-то логично. ES>В целом, что бы не забивать голову лишней информацией и избежать всех возможных проблем — правильно использовать _beginthreadex для создания потока и return 0 для выхода из него. С последующим CloseHandle
Ясно, понял. Спасибо!
1613 г. = 2024 г.
Re: Проблема с потоком, работающим с компонентами диалогового окна
Есть у меня диалог 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]: Проблема с потоком, работающим с компонентами диалогового окна
Здравствуйте, RussianFellow, Вы писали:
RF>Как исправить эту ошибку? Как сделать так, чтобы и при работе с GUI-диалогом (его компонентами) мои потоковые функции нормально работали, то есть чтобы происходило нормальное завершение их работы по флагу?
Я так понял ты используешь поток для вывода бегущей строки? Это перебор.
Вообще лезть в оконные дела из другого потока — это не есть гуд и чревато непредсказуемыми последствиями и в частности дедлоками.
В твоём случае бегущую строку лучше реализовать через таймер. То есть запускаешь таймер SetTimer(1000) и обрабатываешь сообщение WM_TIMER. В обработчике уже выводишь текст как нужно. Система сама будет вызывать обработчик через указанный интервал. Когда нужно прекратить вызов таймера — останавливаешь его с помощью KillTimer.
RF>Может быть, как-то по-другому следует вызывать функцию _beginthreadex или же вместо неё следует пользоваться функцией AfxBeginThread или классом CWinThread ?
Это здесь не при чём.
Re[3]: Проблема с потоком, работающим с компонентами диалогового окна
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>В твоём случае бегущую строку лучше реализовать через таймер. То есть запускаешь таймер SetTimer(1000) и обрабатываешь сообщение WM_TIMER. В обработчике уже выводишь текст как нужно. Система сама будет вызывать обработчик через указанный интервал. Когда нужно прекратить вызов таймера — останавливаешь его с помощью KillTimer.
Ясно. Для того, чтобы запустить таймер, нужно воспользоваться функцией SetTimer, а для того, чтобы выключить таймер, нужно воспользоваться функцией KillTimer:
Но как правильно поступить с таймером, если запустил его один раз, потом пользователь нажал на кнопку "Приостановить" (таймер должен остановить свою работу), затем пользователь нажимает на кнопку "Продолжить" (таймер должен заработать заново), а потом пользователь нажимает на кнопку "Выйти" (таймер должен прекратить свою работу)? Как это правильно организовать?
1613 г. = 2024 г.
Re[4]: Проблема с потоком, работающим с компонентами диалогового окна
Здравствуйте, RussianFellow, Вы писали:
RF>Но как правильно поступить с таймером, если запустил его один раз, потом пользователь нажал на кнопку "Приостановить" (таймер должен остановить свою работу), затем пользователь нажимает на кнопку "Продолжить" (таймер должен заработать заново), а потом пользователь нажимает на кнопку "Выйти" (таймер должен прекратить свою работу)? Как это правильно организовать?
По кнопке "Приостановить" — KillTimer, по кнопке "Продолжить" — SetTimer, по кнопке "Выйти" — KillTimer
Re[5]: Проблема с потоком, работающим с компонентами диалогового окна
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>Здравствуйте, RussianFellow, Вы писали:
RF>>Но как правильно поступить с таймером, если запустил его один раз, потом пользователь нажал на кнопку "Приостановить" (таймер должен остановить свою работу), затем пользователь нажимает на кнопку "Продолжить" (таймер должен заработать заново), а потом пользователь нажимает на кнопку "Выйти" (таймер должен прекратить свою работу)? Как это правильно организовать?
ES>По кнопке "Приостановить" — KillTimer, по кнопке "Продолжить" — SetTimer, по кнопке "Выйти" — KillTimer
Здравствуйте, RussianFellow, Вы писали:
RF>Не подскажете ли, уважаемые коллеги, как лучше всего работать с потоками (threads) в MFC? RF>Есть функция, производящая математические вычисления. Эта функция большая по длине и её действия происходят в цикле до выполнения некоего условия. Эта функция работает в консольном режиме--ввод и вывод данных там происходит с помощью консольного окна. RF>Как лучше поместить эту функцию в поток--использовать класс CWinThread, воспользоваться функцией AfxBeginThread или же использовать функции WinAPI для работы с потоками?
Если в потоке не будет общения с MFC, то как угодно, то есть сейчас это значит, что лучше средствами STD.
Если в потоке будет общение с MFC (в чём я сомневаюсь, потому что для расчётов это не нужно), то создавать поток надо средствами MFC, AfxBeginThread
AfxBeginThread помимо всего прочего инициализирует per-thread структуры данных MFC, поэтому создавать поток, из которого будет использоваться MFC, нужно именно через эту функцию (или аналог).