Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[13.59.220.223] |
|
Страницы: (5) 1 [2] 3 4 ... Последняя » все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
Немодальный Message box http://stackoverflow.com/a/23647706
Цитата Take a look at the function CreateMessageDialog in unit Vcl.Dialogs. This returns a the standard VCL MessageDialog form which you can manipulate, if necessary, and then show, modal or normally, as you wish. А порочность вторичного потока с модальностью UI всё та же - надо уметь понимать есть ли у этого потока сейчас модальный диалог на экране и "нажать ему кнопочку", иначе из цикла прокачки сообщений особо и не выйти. А значит - не сделать graceful-завершение (т.е. либо поток остаётся жить вечно и приложение не сможет завершиться, мешая в том числе и нормальной перезагрузке ОС, либо TerminateThread со всеми минусами что описаны выше). Вообще, при всей своей кажущейся тривиальности, UI-поведение - тема очень и очень многогранная с точки зрения удобства пользователя (user experience, UX). Скажем, ранее я тоже считал, что любой диалог должен быть модальным, всё прочее ересь и неудобство. Но когда я посмотрел на Perforce-клиент (так называемый P4), то был удивлён насколько удобной может быть немодальность диалоговых окон при выполнении тех или иных операций: Сабмитишь? А вот можно зайти в список предыдущих коммитов. Бранчуешь или мёржишь? А вот тебе доступ к общему дереву, к консоли сообщений, к чему угодно. В этом свете стандартные модальные диалоги ОС начинают казаться чугунными кандалами, где можно максимум это лишь погреметь при желании цепью |
Сообщ.
#17
,
|
|
|
Порочность не в самом MessageBox, а в кривой логике взаимодействия потоков - с одной стороны тебе хочется автоудаления потока через FreeOnTerminate, "не вешаться" на OnTerminate и не "устанавливать какую-нибудь переменную вне TThread", а с другой стороны ты хочешь каким-то странным способом контролировать завершение потока и даже закрывать его MessageBox. Во-первых, не нужно никакого FreeOnTerminate. Лучше иметь ссылку на объект потока, в которой уже есть масса полезных свойств типа Finished, Handle, ThreadID, а также можно добавить свои, например признак того, что поток находится в состоянии показа MessageBox. Раз тебе все равно приходится контролировать состояние потока, то и удалить его объект не составит труда, когда установлен флаг Finished (и GetExitCodeThread не возвращает STILL_ACTIVE). Если поток находится в состоянии показа MessageBox, то вывести его из этого состояния можно простым PostThreadMessage(ThreadID,WM_QUIT,0,0). Если он может делать что-то еще, то можно использовать штатные средства Terminate\Terminated для корректного досрочного завершения. PS: Не только вызовы EnumThreadWindows/SendMessage(WM_CLOSE), но и PostThreadMessage(ThreadID,WM_QUIT,0,0) из главного потока по любому выглядят коряво. Лучше в самом классе потока создать метод типа CloseMessageBox (или ExitMessageLoop), в котором будет вызываться PostThreadMessage |
Сообщ.
#18
,
|
|
|
Камрады хорошо ответили, добавить особо нечего.
Что касается немодальной мессаги - hWnd = 0 и всё |
Сообщ.
#19
,
|
|
|
Не хочу я юзать VCL. Можно на WinAPI сделать, конечно. Но так мне видится проще.
Цитата Mr.Delphist @ Не вижу тут проблем. EnumThreadWindows - сработала, значит есть (к тому же, у меня есть отдельный флаг для этого). Кидаешь WM_CLOSE (даже если промахнёшься на микросекунду, ничего страшного всё равно не произойдёт).А порочность вторичного потока с модальностью UI всё та же - надо уметь понимать есть ли у этого потока сейчас модальный диалог на экране и "нажать ему кнопочку", иначе из цикла прокачки сообщений особо и не выйти. Цитата leo @ Я наоборот хотел, чтобы FreeOnTerminate устанавливал ThreadObj в nil. А суть этого вопроса вообще в том, чтобы случайно не обратиться к Synchronize уже освобождённого объекта. Но т.к. от Synchronize я отказался, то и вопрос отпал.Порочность не в самом MessageBox, а в кривой логике взаимодействия потоков - с одной стороны тебе хочется автоудаления потока через FreeOnTerminate, "не вешаться" на OnTerminate и не "устанавливать какую-нибудь переменную вне TThread", а с другой стороны ты хочешь каким-то странным способом контролировать завершение потока и даже закрывать его MessageBox. А в самом потоке перед завершением устанавливаются флаги (вне TThread) - само собой. Цитата leo @ Постоянно контролировать мне его состояние не надо. Только при определённых условиях (принудительном закрытии MesageBox'а). Поэтому FreeOnTerminate пусть будет. Проблем с обнулением тоже нет, т.к. см. предыдущий коммент.Во-первых, не нужно никакого FreeOnTerminate. Лучше иметь ссылку на объект потока, в которой уже есть масса полезных свойств типа Finished, Handle, ThreadID, а также можно добавить свои, например признак того, что поток находится в состоянии показа MessageBox. Раз тебе все равно приходится контролировать состояние потока, то и удалить его объект не составит труда, когда установлен флаг Finished (и GetExitCodeThread не возвращает STILL_ACTIVE). Если поток находится в состоянии показа MessageBox, то вывести его из этого состояния можно простым PostThreadMessage(ThreadID,WM_QUIT,0,0). Если он может делать что-то еще, то можно использовать штатные средства Terminate\Terminated для корректного досрочного завершения. А вот PostThreadMessage(ThreadID,WM_QUIT,0,0) - это проще, чем EnumThreadWindows И про GetExitCodeThread=STILL_ACTIVE может пригодиться Добавлено Подскажите ещё... есть ли где-то список всех активных объектов моего класса? Или самому такой только создавать нужно? Для graceful-завершения как раз... Добавлено Цитата Jin X @ Единственный минус - поток может завершиться между проверкой флага, что поток ещё работает и PostThreadMessage.Поэтому FreeOnTerminate пусть будет. Но это тоже маловероятно, т.к. здесь у меня всего несколько инструкций, а между завершением TThread.Execute и TThread.Free (по FreeOnTerminate) гораздо больше инструкций. Добавлено Мне надо сейчас проработать места, где поток может внезапно завершиться, когда не надо (между проверкой и действием). Вот думаю TThread.Suspend'ами разбавить (до проверки), т.к. всякие там InterlockedXXXX в данном случае не катят (неудобны). |
Сообщ.
#20
,
|
|
|
Цитата Jin X @ Мне надо сейчас проработать места, где поток может внезапно завершиться, когда не надо (между проверкой и действием). Вот думаю TThread.Suspend'ами разбавить (до проверки) Госпидя-я... Решить проблему "внезапного завершения" можно только синхронными методами. А SuspendThread - та же опасная асинхронная операция прерывания потока в неопределенном \ непредсказуемом состоянии, как и TerminateThread. Ты вообще в msdn и дельфийскую справку заглядываешь? Зачем все эти кулхацкерские выкрутасы, если есть штатный синхронный способ контролировать завершение потока через OnTerminate? Раз сохранил какие-то ссылки на поток, то повесь ему OnTerminate для обнуления этих ссылок - и всё! Или тебе какая-то сектантская религия не позволяет использовать Synchronize ни под каким соусом? Цитата Jin X @ А суть этого вопроса вообще в том, чтобы случайно не обратиться к Synchronize уже освобождённого объекта. Но т.к. от Synchronize я отказался, то и вопрос отпал. Метод Synchronize предназначен для вызова из самого потока TThread, а не из главного. Как поток может вызвать Synchronize, если он уже завершил Execute и "самоликвидировался"?! Вызывать Synchronize из главного потока допустимо, но не имеет смысла, т.к. это эквивалентно прямому вызову переданного метода без какой-либо "синхронизации" с потоком, которому принадлежит метод или который передан в качестве параметра в Synchronize(Th, Method). |
Сообщ.
#21
,
|
|
|
Цитата leo @ Меня не именно завершение потока интересует (неверно выразился... вернее, _уже_ особо не интересует), а конкретные места в коде потока, где он выполняет что-то (устанавливает значения, вызывает метод и пр). Т.е. мне надо, чтобы это сделал либо доп.поток, либо метод, вызываемый из основного потока, а не оба сразу. А чем Suspend-то опасен?Госпидя-я... Решить проблему "внезапного завершения" можно только синхронными методами. А SuspendThread - та же опасная асинхронная операция прерывания потока в неопределенном \ непредсказуемом состоянии, как и TerminateThread. Ты вообще в msdn и дельфийскую справку заглядываешь? Зачем все эти кулхацкерские выкрутасы, если есть штатный синхронный способ контролировать завершение потока через OnTerminate? Раз сохранил какие-то ссылки на поток, то повесь ему OnTerminate для обнуления этих ссылок - и всё! Или тебе какая-то сектантская религия не позволяет использовать Synchronize ни под каким соусом? p.s. А вообще, без а-ля InterlockedXXXX проблематично это сделать. Думал двойной установкой переменных обойтись, но уж не буду дурью маяться (ошибиться легко, а смысла особого нет) p.p.s. Synchronize и OnTerminate (который тоже вызывается через Synchronize) мне не нравятся тем, что прога может быть, например, консольной, и тогда этого OnTerminate мы не дождёмся вообще никогда. Цитата leo @ Это понятно. Я хотел вставить этот кусок в метод, который может вызываться как из доп. потока, так и из главного. Метод Synchronize предназначен для вызова из самого потока TThread, а не из главного. Как поток может вызвать Synchronize, если он уже завершил Execute и "самоликвидировался"?! Вызывать Synchronize из главного потока допустимо, но не имеет смысла, т.к. это эквивалентно прямому вызову переданного метода без какой-либо "синхронизации" с потоком, которому принадлежит метод или который передан в качестве параметра в Synchronize(Th, Method). Добавлено Кстати, Synchronize ждёт завершения вызова метода (или просто ставит в очередь)? |
Сообщ.
#22
,
|
|
|
Цитата Jin X @ Кстати, Synchronize ждёт завершения вызова метода (или просто ставит в очередь)? Разумеется, ждет. Иначе это был бы не Synchronize, а что-то другое Цитата Jin X @ А чем Suspend-то опасен? Тем, что он прерывает выполнение потока в неопределенном состоянии, например, при захвате им критической секции. А крит.секции, как суслики - ты их не видишь, а они есть - сидят в потайных норах и дельфийского кода (например, в менеджере памяти), и WinAPI. Вообще, SuspendThread это отладочная функция, которая предназначена для вызова из другого процесса, а не своего собственного. Цитата Jin X @ Synchronize и OnTerminate (который тоже вызывается через Synchronize) мне не нравятся тем, что прога может быть, например, консольной, и тогда этого OnTerminate мы не дождёмся вообще никогда. Час от часу не легче. А вдруг немцы? (С) Synchronize в доп.потоке должна работать в паре с CheckSynchronize в главном потоке. В GUI-приложении вызов CheckSynchronize встроен в цикл обработки Application.Run (точнее в Application.Idle). В консольной проге встроенного механизма нет, поэтому нужно либо вызывать CheckSynchronize периодически, либо там, где нужно по логике работы проги, либо использовать другие методы синхронизации потоков. Цитата Jin X @ Постоянно контролировать мне его состояние не надо. Только при определённых условиях (принудительном закрытии MesageBox'а). Какая разница - постоянно или однократно? Как ты узнаешь, что нужно закрыть MessageBox? Тебе сам поток как-то об этом сигнализирует (или разгневаннаый юзер) или ты по своей инициативе проверяешь наличие этого бокса? Если ты по любому хоть раз, как-то проверяешь состояние потока, то почему-бы его и не удалять из памяти при этой проверке? А если поток завершится раньше, и объект TThread повисит какое-то время памяти, то что в этом страшного? |
Сообщ.
#23
,
|
|
|
Цитата leo @ Так, прервал Suspend выполнение потока в неопределённом состоянии, пусть даже при захвате критической секции... иии???Тем, что он прерывает выполнение потока в неопределенном состоянии, например, при захвате им критической секции. А крит.секции, как суслики - ты их не видишь, а они есть - сидят в потайных норах и дельфийского кода (например, в менеджере памяти), и WinAPI. Вообще, SuspendThread это отладочная функция, которая предназначена для вызова из другого процесса, а не своего собственного. Что плохого-то в этом? Если я вскоре вызову ResumeThread ? Цитата leo @ Ну начинается... Т.е. я, делая модуль, реализующий какой-то функционал в фоновом режиме, должен написать: "Вызывайте CheckSynchronize почаще, пожалуйста, а то могут возникать глюки"? Зачем, если я могу всё это сделать без Synchronize? Synchronize в доп.потоке должна работать в паре с CheckSynchronize в главном потоке. В GUI-приложении вызов CheckSynchronize встроен в цикл обработки Application.Run (точнее в Application.Idle). В консольной проге встроенного механизма нет, поэтому нужно либо вызывать CheckSynchronize периодически, либо там, где нужно по логике работы проги, либо использовать другие методы синхронизации потоков. Цитата leo @ А зачем мне его закрывать? Есть MessageBoxTimeout, который сам закроет окно после определённого таймаута. И есть метод Close, который можно вызвать из программы и закрыть это окно.Какая разница - постоянно или однократно? Как ты узнаешь, что нужно закрыть MessageBox? Тебе сам поток как-то об этом сигнализирует (или разгневаннаый юзер) или ты по своей инициативе проверяешь наличие этого бокса? Цитата leo @ А какой в этом смысл? Зачем мне его удалять из памяти при "этой" проверке? И зачем потоку висеть в памяти какое-то время? Если можно просто сделать FreeOnTerminate := True, а перед завершением потока установить флаг во внешней переменной? Тем более, что устанавливать флаг всё равно нужно, т.к. при всё той же "этой" проверке мне надо как-то узнать, что поток ещё не удалён.Если ты по любому хоть раз, как-то проверяешь состояние потока, то почему-бы его и не удалять из памяти при этой проверке? А если поток завершится раньше, и объект TThread повисит какое-то время памяти, то что в этом страшного? Для чего это нужно? Если можно сделать проще Добавлено leo, разговор уже ушёл далеко от первоначальной темы И мне даже кажется, что мы уже немного говорим не о том. Я как доделаю эту штуку (руки до неё всё никак не дойдут), выложу и можно будет подискутировать где какие косяки Может, кому-то модуль и пригодится... Добавлено Собственно, я сейчас уже всё доделал, осталось только оттестить и отладить. Но это уже не сегодня |
Сообщ.
#24
,
|
|
|
Цитата Jin X @ Что плохого-то в этом? Если я вскоре вызову ResumeThread ? в том, что если ты засуспензил поток, в в момент, когда менеджер памяти залочен в крит секции, то ты ты сам не можешь использовать менеждер памяти. а менеджер памяти используется явно и не явно везде. ты разве что сложить пару чисел можешь без него, не меньше. |
Сообщ.
#25
,
|
|
|
В данном случае я собираюсь (собирался... уже по-человечески, через InterlockedComparExchgane сделал) Suspend'ить доп. поток для проверки переменных, т.е. чтобы "сложить" (сравнить) пару чисел. Мне менеджер памяти не нужен был.
|
Сообщ.
#26
,
|
|
|
Если уж так надо суспендить поток, то делай через виндовые средства (эвенты, семафоры и т.д.) и WaitFor*. Ну или по-топорному, while ThreadPaused do Sleep(100). Это хотя бы безопасно.
А вообще чем дальше, тем меньше я понимаю, чего ты хочешь достичь. Неверная архитектура задачи видится мне. |
Сообщ.
#27
,
|
|
|
Цитата Jin X @ leo, разговор уже ушёл далеко от первоначальной темы. И мне даже кажется, что мы уже немного говорим не о том. Во-первых, первоначальная тема (также как и упомянутая "Найти и закрыть MessageBox"), все больше напоминает сюжет "Саги об X, Y и Z", когда вместо вопроса по сути решаемой задачи, задаются и обсуждаются вопросы, которые не имеют к данной задаче никакого отношения или решают ее, мягко говоря, не лучшим, а то и извращенным способом. А о сути и нюансах самой задачи мы узнаем по крупицам в ходе обсуждения (например, тема начиналась с вопроса о Synchronize, а затем вдруг всплыло консольное приложение, в котором Synchronize без.доп телодвижений работать не может). Мы говорим как раз "о том", но ты упорно придерживаешься выбранного ранее пути решения и приводишь в его оправдание совершенно нелогичные доводы. Тебе сразу сказали, что FreeOnTermiate имеет смысл использовать только в случаях, когда 1) тебя совершенно не интересует "судьба" потока - как говорится, запустил и забыл, 2) используется стандартный способ контроля завершения потока через событие OnTerminate (или через свои примочки типа Post\SendMessage). В остальных случаях, когда возникает желание или необходимость контролировать завершение потока не через OnTerminate, задавать FreeOnTerminate = true, не имеет смысла, т.к. это не упрощает, а лишь усложняет логику взаимодействия. И все твои примочки и блуждания в трех соснах (от Synchronize до Terminate\SuspendTrhead и InterlockedXXX) это лишь подтверждают. Ты в предыдущем посте несколько раз повторил "а зачем?", "проще" и т.п. Нет, не проще. Встречный вопрос: зачем "установить флаг во внешней переменной" "... всё равно нужно", если всё, что нужно уже есть в самой переменной TThread? Разве использовать несколько доп.переменных и самому их устанавливать проще, чем использовать готовый объект, в котором все устанавливается автоматически? Зачем доверять потоку удалять свой объект по FreeOnTerminate? Чтобы потом чесать репу - а удалился ли он уже, или находится в состоянии удаления, и не удалится ли на следующей строчке после проверки какого-то там флага, и городить ради этого огород с SuspendTread, шаманить с Interlocked-функциями и т.п.? Зачем все это? Не проще ли самому удалить объект потока, когда он завершился и больше не нужен (хоть в FormDestroy или в завершающем finally консольной проги)? Или ты считаешь, что любую мало-мальскую память нужно освобождать сразу после ее использования? Типа создал строку, передал ее куда-то - непременно вызови S:='', чтобы она не висела в памяти в ожидании автоочистки? Добавлено Цитата Jin X @ А если этот менеджер памяти мне приспичило использовать в тот момент, когда доп. поток залочит его в критической секции (без Suspend'а), то я смогу его использовать? Он будет ждать когда доп. поток его освободит что ли? Разумеется. На то она и крит.секция, чтобы разрешать выполнять некоторый кусок кода только одному потоку. Поэтому, если ты остановишь поток в крит.секции менеджера памяти, то любое неявное выделение\освобождение памяти между Suspend и Resume (например, при операциях со строками) приведет к зависону. Ты конечно можешь этого избежать, всё "оттестить и отладить", но по любому SuspendTread - это специальная функция, использовать которую в обычных\нормальных приложениях не рекомендуется. Поэтому, юзать ее ты можешь только "на собственный страх и риск для домашнего использования". |
Сообщ.
#28
,
|
|
|
leo, всё просто. Обсуждается тема unit'а по созданию немодального MessageBox, который висит в фоне и закрывается либо по таймауту, либо юзером, либо методом Close. Он может работать в консольном приложении, а не только в GUI. Вот и всё.
Цитата leo @ Я, честно говоря, вообще не понимаю, как ты предлагаешь организовать весь процесс. Смотри, логика проста до безобразия.Ты в предыдущем посте несколько раз повторил "а зачем?", "проще" и т.п. Нет, не проще. Встречный вопрос: зачем "установить флаг во внешней переменной" "... всё равно нужно", если всё, что нужно уже есть в самой переменной TThread? Разве использовать несколько доп.переменных и самому их устанавливать проще, чем использовать готовый объект, в котором все устанавливается автоматически? Зачем доверять потоку удалять свой объект по FreeOnTerminate? Чтобы потом чесать репу - а удалился ли он уже, или находится в состоянии удаления, и не удалится ли на следующей строчке после проверки какого-то там флага, и городить ради этого огород с SuspendTread, шаманить с Interlocked-функциями и т.п.? Зачем все это? Не проще ли самому удалить объект потока, когда он завершился и больше не нужен (хоть в FormDestroy или в завершающем finally консольной проги)? Есть метод Show, который создаёт поток (с FreeOnTerminate) и устанавливает переменную (в классе) Status в значение Displaying - таким образом и сам класс и использующий его код будет знать, что окно отображается. Поток создаёт сообщение MessageBoxTimeout, после завершения которого (и непосредственно перед выходом) устанавливает Status в другое значение (Timedout, Button или Breaked). Таким образом я (класс) знаю, есть ли работающий поток, а программа (вызывающая класс) знает, отображается ли сообщение в данный момент. Метод Close (принудительное закрытие) проверяет - Displaying ли сейчас окно? Если да, то посылает ему PostThreadMessage(WM_QUIT). Это всё. Вопрос: зачем мне нужно сделать FreeOnTerminate := False и самому его освобождать? Единственным доводом в этом пользу может служить разве что "вдруг поток освободиться в Close между проверкой Displaying и PostThreadMessage, и тогда я обращусь к несуществующему Thread.Handle (при вызове PostThreadMessage)?" Но и тут есть простое решение: в этом случае поток может сделать сам себе FreeOnTerminate := False, а Close сделать Thread.Free. Это единственный момент, когда это может быть оправдано, на мой взгляд. Если ты другого мнения, пожалуйста, расскажи. Потому что пока я не понимаю смысла делать иначе . Да, можно делать Free именно в Show, но чем это лучше? Цитата leo @ Вот это тут вообще ни причём. Другое дело, что если есть выбор (и это не усложняет код и не делает его менее надёжным), лучше освободить, не так ли? Или ты считаешь, что любую мало-мальскую память нужно освобождать сразу после ее использования? Типа создал строку, передал ее куда-то - непременно вызови S:='', чтобы она не висела в памяти в ожидании автоочистки? Добавлено Цитата leo @ Блуждание произошло из-за того, что я вместо Interlocked зачем-то решил использовать двойные флаги и/или Suspend, чем сейчас и сам удивляюсь.В остальных случаях, когда возникает желание или необходимость контролировать завершение потока не через OnTerminate, задавать FreeOnTerminate = true, не имеет смысла, т.к. это не упрощает, а лишь усложняет логику взаимодействия. И все твои примочки и блуждания в трех соснах (от Synchronize до Terminate\SuspendTrhead и InterlockedXXX) это лишь подтверждают. А Synchronize нужен был для оповещения основной программы о том, что "произошёл запуск" или "окно закрылось". Типа OnXXXX и иже с ними. Сначала хотел сделать 2 варианта: прямой вызов и через Synchronize, потом решил сделать только вызов напрямую, а программист уже сам решит: нужен ему Synchronize или нет и вызовет его. |
Сообщ.
#29
,
|
|
|
Вот такой вопрос у меня ещё: если я выполняю какую-то операцию (например, PostThreadMessage) с потоком, который только что завершил свою работу, ничего страшного, как я понимаю, не произойдёт.
Но! Может ли возникнуть ситуация, что создастся поток с таким же Id и я закрою что-то другое? Каким образом эти Id генерируются? |
Сообщ.
#30
,
|
|
|
Цитата Jin X @ Обсуждается тема unit'а по созданию немодального MessageBox, который висит в фоне и закрывается либо по таймауту, либо юзером, либо методом Close. Он может работать в консольном приложении, а не только в GUI. Вот и всё. Тьфу ты блин, сразу сказать не судьба была? https://github.com/Fr0sT-Brutal/Delphi_DlgCountdown |