Суть задачи: есть функция, которая вычисляет чтото. Функция работает весьма долго. Надо пользователю предоставлять окно с инфой о прогрессе (хотя прогресс совсем не линеен, но хоть чтото) и возможностью отмены расчета. Сделал так: основной поток, который должен вернуцо в вызвавший код, создает 1 поток - расчетный. Дальше WaitForSingleObject на хэндл. Расчетный поток создает поток диалогового окна, которое сообщает о прогрессе. На окне кнопка отмены расчета. По кнопке киляется поток расчета. Поток диалога киляется после выхода из WaitForSingleObject. Собсно комбинаций много перепробовал для убийств. Какие случаи бывают - если в потоке диалога делаю ExitThread - то после 2х вызовов расчета подряд поток не умирает. Расчетный поток убивается, а диалоговый висит. Если делаю TerminateThread после Wait, то при следующем вызове есть 3 потока, но диалога нету... Вариаций хватает... Главная проблема: основной поток расчета после нескольких пересчетов зависает. Зависает вместе с диалогом... Причем я долго не мог понять где. Судя по всему вешается он на new. Должен заметить, расчет активно использует new и delete. В мсдн нашел интересное место 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. Собсно вопрос - что делать? Как рулить долгим потоком...
Код (Text): // начинаем поток расчета calc_thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)CalcThreadProc, 0, 0, &calcthread_id); // ждем окончания расчета, или же отмены расчета WaitForSingleObject(calc_thread, INFINITE); TerminateThread(waitdialog_thread, -1); GetExitCodeThread(calc_thread, &calcthread_exit_code); if(calcthread_exit_code == -1){ // в расчете произошла ошибка, или расчет отменили NumOfPlates = 0; } else{ calculator->ExtractPlates(Plate_Points_X, Plate_Points_Y, Plates_Height); NumOfPlates = plates_num; } Код (Text): void CalcThreadProc () { plates_num = 0; waitdialog_thread = 0; DWORD tid; waitdialog_thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ShowWaitDialog, 0, 0, &tid); plates_num = calculator->Calculate(); SendMessage(waitdialog_wnd, WM_CLOSE, 0, 0); //KillTimer(waitdialog_wnd, 0); //EndDialog(waitdialog_wnd, -1); //TerminateThread(waitdialog_thread, 0); ExitThread(0); } Код (Text): BOOL CALLBACK DialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_INITDIALOG: waitdialog_wnd = hDlg; SetTimer(hDlg, 0, 100, 0); return (TRUE); case WM_DESTROY: case WM_CLOSE: KillTimer(hDlg, 0); TerminateThread(calc_thread, -1); waitdialog_wnd = 0; EndDialog(hDlg, -1); //ExitThread(-1); return (TRUE); case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_CANCEL: KillTimer(hDlg, 0); TerminateThread(calc_thread, -1); waitdialog_wnd = 0; EndDialog(hDlg, -1); //ExitThread(-1); return (TRUE); } return (FALSE); } return (FALSE); }
>>Главная проблема: основной поток расчета после нескольких пересчетов зависает. Зависает вместе с диалогом... >>Причем я долго не мог понять где. Судя по всему вешается он на new. Должен заметить, расчет активно использует >>new и delete. На счет CRT Рихтер говорит: "Чтобы многопоточные программы, использующие библиотеку С/С++, работали корректно, требуется создать специальную структуру данных и связать ее с каждым потоком, из которого вызываются библиотечные функции Более того, они должны знать, что, когда Вы к ним обращаетесь, нужно просматривать этот блок данных в вызывающем потоке чтобы не повредить данные в каком-нибудь другом потоке Так откуда же система знает, что при создании нового потока надо создать и этот блок данных. Ответ очень прост не знает и знать не хочет Вся ответственность — исключительно на Вас Если Вы пользуетесь небезопасными в многопоточной среде функциями, то должны создавать потоки библиотечной функцией _begmthreadex, а не Windows-функцией CreateThread"
я new и delete'ю класцы и структуры. мне проще добавить глобальные флаги, которые бы сообщали о сбросе расчета. и в функции расчета просто на итерациях (правда во многих циклах) проверять этот флаг. потому как косяк 99.999% именно в работе с ъхипом, а именно в моменты киляния потока, находящегося в кернеле. т.е. мне проще пожертвовать несколькими секундами паузы между нажатием на конпку Cancel и реальной остановкой потока, но гарантировано человечной, нежели переписывать кодес =) или вариант морозить гуи поток на моменты вызова new и delete, перегрузив их. чтобы не попасца в неудобный момент. тока это имхо больше ресурсов требует. зы: пока не хочеца парица со структурами для потоков, ибо заказчик просит не задерживаца, а тема для меня новая, лучше как нить по деревенски сделоть. жду рекомендаций. седня уже спать буду, завтра с утреца приступлю.
короч исправил по способу добавления флага останова. муторно и потери скорости канешн будут, но пока не замерял. самое главное - работает все нормально. Код (Text): calc->SetInterruptCalc(true); WaitForSingleObject(calc_thread, INFINITE); waitdialog_wnd = 0; EndDialog(hDlg, -1); и внутри класса Код (Text): if(interrupt_calc == true){ ExitThread(-1); } Типа диалог висит, пока поток расчета не обработает флаг останова. Красивше делать некогда, может потом. ЗЫ: спасибо за советы
Rascalspb Я бы вообще по-другому написал: в первом потоке создается диалог с прогресс-баром и вертится цикл обработки сообщений, во втором есть хэндл прогрессбара. А вообще код у вас такой же как язык (албанский). _beginthreadex, если пишете с применением CRT. Такое вообще не советуют делать. Лучше return 0, а еще лучше _endthreadex(0); Зачем? Не лучше ли послать сигнал о том, что надо "сворачиваться"? MSDN: ThreadProc callback function Наверняка что-нибудь еще пропустил.
censored Я предпочитаю использовать апи. из crt - тока new и delete, ито ибо пишу с классами. На счет завершения потока - сигналить то сигналить, но в функции много потенциально опасных мест, в которых может повесицо поток. В этом смысле канешно джава рулит, и поэтому все равно придеца сделать кнопку в окошке ожидания - hard kill. Хотя итак много мест зависающих исправил. ну я прописал ExitThread, а у трэдпроца возвращаемое значение важно лишь как сигнал о коде завершения потока. Так что в принципе работать будет итак, но канешн некрасиво - поправлю хз. покажи, как это поможет. главный косяк я описал - терминатить тупо незя - иначе косяки в кернеле. как тлс может помочь убить поток в первый же удобный момент(када снялись все блокировки, вышли из всех критических секций итп), но без проверок в каждом цикле, работающим в потоке
>>из crt - тока new и delete, ито ибо пишу с классами Если предпочитаешь АПИ то не используй вообще CRT(пиши свой new delete, если есть глоб. экземпляры классов пиши свои реализации инициализации/завершения, если есть код с плавающей точкой тоже нужна инициализация среды обработки ошибок и тд. ). Иначе: Как вариант для чистого АПИ в VC можно использовать _ATL_MIN_CRT и тогда будет тебе немного этого вкусного безопасного CRT.