Delphi чтобы приложение не зависало
Конфигурация компьютера | |
Процессор: Intel Core i3 @ 3333 MHz | |
Материнская плата: Asus P7H55-M PRO | |
Память: Hynix DDR3-1333 2x4 GB @ 667 MHz | |
HDD: Seagate ST3250318AS 250 GB + WDC WD5000AADS 500GB | |
Видеокарта: PowerColor Radeon HD 4650 512 MB | |
Звук: SB Audigy | |
Монитор: AOC 1719 @ 1280x1024 | |
ОС: Genuine Windows 7 Профессиональная x64 | |
Индекс производительности Windows: 5,0 |
Вопрос наверняка классический, но все-таки.
Есть форма, на форме кнопка, запускающая (и, в теории, останавливающая) некоторую обработку, и прогрессбар, показывающий процент выполнения. Во время выполнения обработки форма нещадно зависает.
Дальше идет лирика, этот абзац можно пропустить.
Для решения этой проблемы сначала я использовал Application.ProcessMessages, но это скорее костыль чем решение, ибо если в не получается его часто вызывать, то обновление формы происходит как минимум пару раз за все время, чего явно недостаточно чтобы, например, нажать на стоп.
Следующим приближением стало использование TThread. При запуске обработки в отдельном треде ситуация почему-то не изменилась. Форма как зависала, так и зависает, да ещё и выполнение обработки замедлилось.
CreateThread и WaitForSingleObject из WinAPI частично решили проблему, т.е. форма не виснет, но я не могу понять как завершить тред принудительно, все способы которые я на данный момент вычитал в интернетах не сработали.
Помогите разобраться, как завершить поток принудительно?-------
Главное качество любого интеллигентного человека это умение засунуть своё мнение в задницу. другому :)
Как сделать, чтобы программа не зависала в процессе выполнения
Доброго времени суток! У меня есть программа по созданию AVI из кадров, так в тот момент, когда.
Как сделать чтобы программа не зависала пока ожидается подключение?
У меня во время выполнения кода сервера программа намертво зависает пока она ждёт пользователя. Как.
Как сделать что бы пробел считался и программа не зависала?
Здравствуйте! Помогите с вводом данных в файл. Как сделать что бы пробел считался и программа не.
Как сделать, чтобы форма не зависала во время отладки для мониторинга ее работы
привет, помогите пож-та, как сделать чтобы при отладке прогр на форме происходили изменения? Т.е.
В Диспетчере задач - загрузка ЦП = 99 %. Так и должно быть? Или можно как-то это избежать? Как можно решить проблему с зависание программы на время выполнения цикла.
Вставив в цикл Application.ProcessMessages
P.S.
Ну и не стоит выводить данные на экран до окончания цикла. Быстрее работать будет.
Как это можно сделать, ?если по нажатию кнопки?, то прога висит и ни чего не нажимается.
Добавлено через 1 минуту
Ну и не стоит выводить данные на экран до окончания цикла. Быстрее работать будет. Понятненько, все.
Цикл идет, запись в таблицу. Как тогда во время цикла данные не закидывать в таблицу, а потом их вставить все 100000? Вставить в тело цикла условие выхода из него, но уж никак не нажатие кнопки, а логическое условие и его не из пальца нужно высасывать, а вставлять если того требует программа.
типа
Как тогда во время цикла данные не закидывать в таблицу, а потом их вставить все 100000?
Добавлено через 42 секунды
более сложный путь - организовывать долгий цикл в потоке
Добавлено через 42 секунды
Мало одного геморроя?
Во время выполнения цикла данные записываются в StringGrid. Т.е. один цикл прошел - записал значения в SG. И так много раз)
Это еще что. Я сейчас эту таблицу поставил на проверку (сравнение столбцов, в столбце 10 строк. 100 000 столбцов *100 000 проверок = 10 000 000 000) уже два часа комп делает свое дело. Анализ нужен.
Судя по аватарке можешь не успеть.
Судя по аватарке можешь не успеть.
мне надо очень успеть. это важно. Что тут теперь сделаешь. самые большие тормоза в данном процессе - из-за отображения 1исколько-тонулей значений, если они берутся из файла - работайте сразу с файлом, а на вывод только результат, если требуется "всех посмотреть", тогда и выводите
:pardon. пилите, Шура, пилите (с)
о время выполнения цикла данные записываются в StringGrid. Т.е. один цикл прошел - записал значения в SG. И так много раз) Сделай на это время грид невидимым, например. А проверки это копейки. Puporev, подскажи пожалуйста. У меня выполняется цикл. Как мне его выполнение поставить на паузу? И если можно, поработать с другой частью программы. А потом возобновить работу цикла и ждать себе спокойненько, пока цикл дойдет до конца. две глобальные переменные, в одной флаг выхода из цикла в другой шаг цикла step:integer, flag:booleanцикл в процедуре с параметром шаг цикла procedure cicle(st:integer)
затем когда надо вызвал процедуру например впервые с параметром 0
в цикле проверка флаг поднят или нет
например на buttonclick вешаешь поднять флаг flag:=true;
о время выполнения цикла данные записываются в StringGrid. Т.е. один цикл прошел - записал значения в SG. И так много раз) Ну если вы хотели найти самое неудачное и медленное решение, вам это удалось на 100%.
Вот нафига вам стрингрид? Вы хотите что-то изменить в данных или вам просто нравится наблюдать за мельканием чисел в ячейках?
Может полностью задачу озвучите? Вряд ли. За 7 с лихером лет ТС или решение нашел или забил на это.
gidrotr0nik, вот честное слово - гон гонимый. И сами так не делайте, и другим не советуйте!
Зависает программа при выполнении цикла
Здравствуйте, на 3 кнопки написаны циклы, при их выполнении программа зависает, подскажите в чем.
При повторном выполнении цикла таймера, программа зависает
Здравствуйте! Я давно не программировал на делфи, взялся за глобальный проект (дипломная работа в.
Как сделать чтобы если при выполнении метода вылетала ошибка то выводился Алерт?
В метод подается число 10, находит в базе, затем возвращает значения и выводит true. Подается число.
Программа при выполнении каждого цикла выводит по 2 раза строки
Вопрос в следуюшим, программа при выполнении каждого цикла выводит по 2 раза строки. Где я не.
Если же ты ждёшь пока отработает другой процесс, например пока файл не освободится или пока внешняя программа не завершит работу или ещё какое действие, то разумно подойти немного по другому:
Как часто зависит от того насколько длинные циклы. Если считается напряжная математика и циклы очень большие, то если вызывать при каждом проходе цикла то будет сильное замедление.ProcessMessages почти не тормозит систему, можно легко убедиться если сделать тест, получается несколько миллионов вызовов в секунду.
1/5 секунды достаточно малое время и торможения реакции на мышь и клавиатуру не заметно, в тоже время при такой частоте замедления работы циклов не будет.А вот это наоборот очень заметно, сильная не ритмичность при движении мышки, при перемещении окна.
Чего вызывать? И главное зачем?
Выполнялся с и без Application.ProcessMessages. На разница в скорости составила в аккурат в 2 раза. Так это операции с файлами, которые в общем-то довольно долгие сами по себе.
Теперь по поводу математики, о которой я говорил, вот пример кода
С использованием Application.ProcessMessages цикл работает на 4 порядка медленнее!
А вот это наоборот очень заметно, сильная не ритмичность при движении мышки, при перемещении окна.А ты пробовал? Я пробовал на если делать Application.ProcessMessages 10 раз в секунду то я не замечаю вообще никакой тормознутости, если 5 раз в секунду - ощущается, но только если присматриваться.
Я пробовал, на моем старом компьютере P3 650 цикл в миллион выполнился за три секунды, на новом 2.4 ггц за 1,5 секунды.
По моему не о чем беспокоиться 1,5 секунды на 1 000 000 вызовов, особенно когда надо просто таскать окно.
Нет, не правильно . Есть один случай, когда Application.ProcessMessages надо использовать с осторожностью: когда мы имеем очень длинный цикл, который делает внутри какую-то очень быструю операцию, тогда время затраченное на выполнение Application.ProcessMessages оказывается сравнимо или больше, чем основной код. Как правильно заметил Anatoly Podgoretsky, на циклах меньше миллиона иттераций можно применять Application.ProcessMess без всяких проблем. Рассмотрим несколько примеров:
For i:=0 to 100000000 do
begin
j:=i+j;
Application.ProcessMessages ;
end;
Здесь мы имеем очень длинный цикл: 100 миллионов раз делается простое арифметическое действие и Application.ProcessMessages. Действие j:=i+j очень простое, и компьютер выполняет его на 4-5 порядков быстрее чем Application.ProcessMessages, следовательно в этом коде 99.99\% времени выполнения прийдётся на выполнение баластной операции Application.ProcessMessages, так как цикл долгий, то разница во времени выполнения - пара секунд и пара часов будет слишком существенной, чтобы необращать на неё внимание. Следовательно этот код не годится, переделываем его примерно таким образом:
For i:=0 to 100000000 do
begin
j:=i+j;
if i mod 1000 = 0 then Application.ProcessMessages;
end;
Теперь приложение не будет выглядеть зависшим, но Application.ProcessMessages будет занимать лишь несколько процентов времени работы программы и замедление программы с лихвой компенсируется удобностью работы.
Теперь другой случай:
For i:=0 to 100000 do
begin
j:=i+j;
Application.ProcessMessages ;
end;
Код похож, только количество проходов невелико - хоть с Application.ProcessMessages, хоть без него он будет выполнятся практически мгноменно. Можете ставить его, можете не ставить - разницы нет.
Допустим мы запускаем квери на базе данных:
For i:=0 to 100 do
begin
Query.чего-то-там-делаем-и-запускаем;
Application.ProcessMessages ;
end;
Запуск квери - очень долгая операция по сравнению с Application.ProcessMessages, даже самая быстрая квери на несколько порядков медленнее, чем Application.ProcessMessages - здесь поставите вы Application.ProcessMessages или нет - выигрышь в скорости выполнения будет 0.001\% в самом лучшем случае, поэтому желательно Application.ProcessMessages в данном случае ставить.
Теперь ещё один момент: допустим у вас есть цикл - любой из них, а как его прервать, например, чтобы кликнуть кнопку "Stop" и цикл прервался? Без Application.ProcessMessages это сделать трудно.
Вот как мы это реализуем, возьмём за основу следующий код:
For i:=0 to 100000000 do
begin
j:=i+j;
if i mod 1000 = 0 then Application.ProcessMessages;
end;
Теперь объявим глобальную переменную Canceled:boolean;
На кнопку "Start" "повесим" следующий код:
Canceled:=False;
For i:=0 to 100000000 do
begin
j:=i+j;
if i mod 1000 = 0 then
begin
Application.ProcessMessages;
if Canceled then
begin
ShowMessage('Loop has been interrupted!')
Break;
end;
end;
end;
На кнопку "Stop" "повесим" следующий код:
Теперь нажатие кнопки Start - запустит цикл, а кнопкой Stop этот цикл можно досрочно прекратить.
. when altering one's mind becomes as easy as programming a computer, what does it mean to be human.
22 мая 2017 г.
Дело о зависании Delphi 7
Очередное детективное расследование. В этот раз мы разбираемся, почему Delphi 7 наглухо виснет при старте.
К нам (в техподдержку EurekaLog) поступил очередной запрос, который касался Delphi 7. Для его решения мне требовалось запустить проект клиента в Delphi 7. Однако, когда я запустил Delphi 7 в нашей тестовой виртуальной машине (на Windows 10 Creators Update) - она зависла при старте. Висит сплэш-скрин, грузится процессор, ничего не происходит.
Таким образом, прежде чем решать проблему с клиентом, нужно решить проблему с самой Delphi 7.
Я проверил, что Delphi 6 и Delphi 2005 (ближайшие смежные соседи Delphi 7) запускаются отлично. Не так давно система Windows 10 на тестовой виртуальной машине была обновлена до Creators Update, что (в очередной раз) сломало регистрацию Delphi 6 (похоже, она привязывается к сборке ОС?). Возможно, что что-то случилось и с Delphi 7?
Быстрое гугление по симптомам (Delphi 7 hangs on Windows 10 Creators Update) не принесло результатов. Похоже, что с проблемой никто не сталкивался. Что-ж, программисты мы или где?
Полная загрузка процессора говорит о том, что у нас не зависание (deadlock), а live lock. Если бы у нас было зависание - мы могли бы воспользоваться уже известными трюками. Но у нас нет зависания, Delphi 7 чем-то занята. Поэтому трюки из статьи по ссылке нам не очень-то помогут (но вам никто не запрещает попробовать).
Итак, запускаем целевое приложение (Delphi 7) и даём ему повиснуть. Запускаем лучшую IDE всех времён и народов (Delphi XE) и вызываем команду Attach to process:
Выбираем наше зависшее приложение и не забываем установить галочку "Pause after attach":
Примечание: если вы собираетесь отлаживать проблему в той же IDE, которой вы хотите производить отладку, то вы можете идентифицировать целевой процесс по PID-у (Process ID), предварительно проверив его через менеджер процессов типа Process Explorer.
IDE подключится к целевому процессу и встанет на паузу. Вы должны увидеть что-то такое:
Примечание: поскольку мы отлаживаем Delphi 7, которая не имеет отладочной информации, то мы сможем работать только с машинным CPU-отладчиком, а стек вызова сможет показывать только подпрограммы из системных DLL и BPL-пакетов (по экспорту). Если же вы отлаживаете современную IDE или свою собственную программу, то отладочная информация у вас будет - или из .jdbg файлов для IDE или из .dcu для вашей программы. Тогда вы сможете использовать и обычный высокоуровневый отладчик (включая анализ переменных, их имена, имена подпрограмм в стеке вызовов и т.п.).
Итак, перед вами - служебный поток отладчика IDE, который тот внедрил в целевую программу, чтобы остановить её. Этот служебный код не отражает никакой реальной работы самой целевой программы. Чтобы начать работу с самой программой, вам нужно сначала переключиться в какой-либо из её рабочих потоков. Для этого посмотрите на окно Threads:
В программе уже запущена куча потоков. Последний поток - служебный, от отладчика вашей IDE. Как я уже сказал, его можно игнорировать. Первый поток (как правило) - главный. Остальные потоки - какие-то фоновые рабочие потоки целевой программы.
Чтобы нам было проще ориентироваться - мы можем назначить (произвольное) имя каждому потоку. Для этого щёлкните правой по потоку и выберите Name thread:
Введите какое-нибудь понятное вам описание потока. Например, "Debugger Thread" или даже "Главный поток".
Далее, дважды щёлкните по следующему потоку. В моём случае следующие четыре потока были одинаковы:
Можно догадаться, что это - служебные фоновые потоки системы, вероятнее всего, обслуживающие системный пул потоков или что-то аналогичное. Иными словами, мы можем их игнорировать.
Первый по счёту поток - главный, что также видно по его (большому) стеку:
Наконец, последний поток:
Судя по всему, это фоновый парсер, который выполняет разбор кода в редакторе кода и, возможно, его подсветку. Наличие такого потока говорит нам о том, что среда загрузилась довольно далеко, прежде чем зависнуть. Мы также можем увидеть, что в настоящее время поток спит ( Sleep / ZwDelayExecution на вершине стека) - вероятнее всего, в ожидании ввода пользователя. Таким образом, мы также можем его игнорировать.
Из всех этих потоков нас пока интересует только главный поток. Начнём его препарировать. Нам известно, что в целевой программе произошло какое-то зацикливание (live lock). Для начала нам нужно определить примерное место. Для этого можно установить точку останова в каком-либо "подозрительном" месте стека вызова. Например:
Заходим с другой стороны:
Ставим программу на паузу, переключаемся в главный поток, ставим кучу точек останова вдоль по стеку и запускам программу снова.
Выяснилось, что программа останавливается в единственном месте - тут:
Это единственное место, и оно находится на самой вершине стека. Это говорит нам о том, что зацикливание, вероятно, связано с, буквально, бесконечным локальным циклом, а не какой-то высокоуровневой ошибкой логики.
Пройдёмся немного по шагам (через "Step Over"), увидим вот это:
Выполнив эту строчку, получим:
Т.е. в конце стоит безусловный переход на начало этого же блока кода. Что это напоминает? Конечно же, цикл вида:
Иными словами, что-то пошло не так и условие выхода из цикла никогда не выполняется, цикл крутится бесконечно (ну или пока целевая программа не вылетит с ошибкой типа нехватки памяти, чтения/записи недопустимой памяти и т.п.).
Кстати, в этот момент нам наконец-то станет известно точное имя подпрограммы, где находится этот цикл:
В этот момент мы можем схитрить и просто открыть файл Controls.pas из папки Source установленной Delphi 7, чтобы найти там метод TDockTree.LoadFromStream . Но это не спортивно и мне не удастся показать несколько приёмов отладки.
Поэтому вместо этого мы продолжаем сессию отладки. Идея следующая: мы искусственно выходим из цикла и смотрим, что при этом произойдёт.
Для этого, нам сначала нужно установить точку останова также и на вызывающем:
Таким образом, как только мы успешно выйдем из цикла - мы встанем на этой точке останова, что и скажет нам о том, что мы успешно продолжили выполнение.
Чтобы не нарушить естественных ход кода программы, нам нужно найти конец цикла и условие, по которому мы могли бы выйти. В данном случае:
Как можно заметить, непосредственно перед переходом на начало цикла стоит вызов какой-то подпрограммы и проверка её результата. В одном случае (как это происходит) - мы продолжим выполнение (и перейдём на начало цикла), в другом случае (который мы хотим запустить сами) - оператор безусловного перехода на начало цикла пропускается и выполнение идёт дальше - с инструкции, непосредственно следующей за оператором безусловного перехода.
Также отметим, что условие заключается в проверке регистра EAX на ноль. Таким образом, чтобы переключиться на другую ветку, нам необходимо изменить значение регистра EAX на отличное от нуля непосредственно до выполнения проверки (но после вызова функции):
(Хотя в данном случае было бы быстрее просто использовать команду "Increment Register")
Выполним команду проверки регистра EAX и увидим, что переход станет активным:
Что ж, запустим программу снова и. остановимся на ровно той же точке останова. Это говорит нам о том, что мы, вероятно, неверно определили конец цикла. Иными словами:
Чтобы найти нужную границу, лучше всего начать с конца метода и найти пути, ведущие к его выходу. Затем переключить все условия, стоящие на этом пути.
Конец метода можно опознать по команде ret , а также по finally -блоку от try (набор pop и mov fs:[eax],edx ). Как правило, "волшебные" finally -блоки стоят в строке с end в конце метода, чтобы освободить ресурсы под локальные переменные с автоматическим временем жизни, либо как явный finally -блок в коде программы перед end .
В данном случае:
Горизонтальными линиями я отметил границы цикла while , а также условие с break , которое перебросит нас за цикл while , в конец метода.
Посмотрим, что же это за условие. Остановимся на команде сравнения и посмотрим, с чем сравнивается значение регистра EAX . Для этого воспользуемся окном Memory и командой Goto Address:
Сразу же обратим внимание, что идёт сравнение с памятью, адрес которой записан непосредственно в коде (т.е. он фиксирован). Т.е. это не локальная и не динамическая переменная. Иными словами, это - глобальная константа или переменная.
Чтобы не запутаться в little/big-endian - удобно переключить отображение на размер, соответствующий нашим данным. В этом случае - 4 байта (a.k.a. DWORD):
Окей, идёт сравнение с $FFFFFFFF - что есть -1 для знакового целочисленного типа. Мы уже выяснили, что это - что-то глобальное (константа или переменная). Поскольку константы целочисленных типов сохраняются непосредственно в коде (на них не производится ссылка через адрес), то у нас, скорее всего, идёт сравнение с глобальной переменной. Ну или с таким:
Но скорее всего - глобальная переменная. Т.е. возможны два варианта: либо в переменную записали что-то не то, либо в данных для цикла отсутствует ожидаемое значение.
В любом случае, мы установили, что для выхода из цикла необходимо, чтобы EAX был равен $FFFFFFFF (вместо его текущего значения: нуля). Окей, остановимся непосредственно перед выполнением проверки и изменим значение EAX :
(Опять же, в данном случае было бы быстрее просто использовать команду Decrement Register)
Запускаем программу на выполнение - и программа останавливается на точке останова в вызывающем. Окей, из цикла мы вышли. Продолжаем выполнение - программа снова останавливается на нашей первой точке остановка (в LoadFromStream ). Т.е. проблемный метод вызывается несколько раз. Повторим действия по искусственному выходу из цикла while . В итоге целевая программа всё же запускается:
Как вы можете видеть, есть артефакты панелей IDE (что, видимо, также вызвало Access Violation). Но, главное, что мы узнали - при старте IDE открывает какой-то старый проект и стопорится при загрузке настроек расположения окон и панелей. Вероятно, эти настройки повреждены. И есть ненулевая вероятность, что эти настройки хранятся в настройках проекта.
Удаляем старый проект, запускаем Delphi 7 - ура! Работает!
Но в чём же была проблема? Давайте посмотрим исходный код. Открываем Controls.pas из Delphi 7 (не Delphi XE):
Ищем в нём TDockTree.LoadFromStream :
А вот, похоже, и наша глобальная переменная из проверки, которую мы меняли, чтобы выйти из цикла while . Как мы можем видеть, -1 - это значение по умолчанию и, следовательно, не является ошибочным значением. Т.е. с переменной всё в порядке, проблема была только в данных.
Смотрим текст метода:
Я выделил границы цикла while и условие выхода из него (которое мы меняли).
Упражнение/домашнее задание: видите ли вы баг в TDockTree.LoadFromStream , который привёл к зацикливанию?
Подсказка: этот пост помечен тэгом "обработка ошибок".
(Этот баг исправлен в RAD Studio 10.2 Tokyo, но мне лень смотреть, в какой именно версии Delphi он был исправлен.)
Репутация: нет
Всего: нет
Помогите решить проблему зависания приложения.
Суть приложения:
Цикл каждые 3 мин
- ClientSocket поочередно подключается к 5 железякам, опрашивает, парсит данные, заносит в 2 таблицы
- idTelnet подключается к одному серверу, делает 8 запросов, парсит, заносит в другие 2 таблицы.
- ServerSocket позволяет подключаться клиентам, после зарешения всех опросов (ClientSocket и idTelnet), данные из таблиц передаются подключенным клиентам (2-6 клиентов).
>
Все это работает стабильно в течении суток+-.
Потом окно перестает обновляться, реагировать на закрытие.
Process Explorer показывает 6 потоков:
- 4 с именем программы - Wait:UserRequest
- WS2_32.dll!SockAsyncTread - Wait:UserRequest
- kernel32.dll!BaseThreadStartThunk - Wait:WrLpcReceive
Если в каждую процедуру и функцию понатыкать логирование начала и конца, то лог заканчивается всегда на работе таймера. Данный таймер каждый раз сбрасывается после опросов и служит для читаемого отображения отсчета 3х минут (т.е. интервал в 180 сек показывает в виде mm:ss в поле Edit).
Репутация: 6
Всего: 72
MadExcept + включение отладочной информации в помощь. При зависании создастся баг-репорт, содержащий полную информацию - что, в какой последовательности вызывалось и на какой строчке зависло.
А что с хендлами и private bytes?
Добавлено через 2 минуты и 11 секунд
В дополнение:
Случайно в ходе этого Application.ProcessMessages не используется?
Репутация: нет
Всего: нет
MadExcept + включение отладочной информации - сделал, придется подождать денек.
Application.ProcessMessages не совсем понятно куда добавлять.
ClientSocket работает в NonBlocking. Шлю комманду, полученные данные парсю.
Раньше не приходилось работать с отладкой, поэтому хендлы не просматривал, завтра как зависнет - гляну.
Присоединённый файл ( Кол-во скачиваний: 6 )
perf.JPG 246,66 Kb
Репутация: 6
Всего: 72
Никуда не надо, наоборот - от этого костыля, если он есть, надо избавляться.
Обычно его использование говорит о. о многом
меню Project => Madexcept settings => Enable Madexcept (или что-нибудь в этом духе, у меня 3.0, а они вроде как уже 4-ю версию выпустили) галочка поставлена?
Там же - check for frozen main thread включено?
Если да - тогда ждем багрепорт.
Репутация: 2
Всего: 459
Неуместное использование говорит о многом. В случае когда используется функция WSAAsyncSelect для обработки уведомлений асинхронного сокета в главном потоке, то Application.ProcessMessages как раз таки будет уместно бороться с зависаниями типа дедлока (хотя эффективнее использовать MsgWaitForMultipleObjects с таймаутом). Но в данном случае, ИМХО ситуация больше похожа, на то что одна из сторон аварийно закрыла соединение, а вторая продолжает думать, что соединение еще живое, шлет данные в пустоту и ждет ответа. В этом случае нужно проверять результаты функций recv / send логировать в случае неуспеха.
гениальность идеи состоит в том, что ее невозможно придумать
Репутация: нет
Всего: нет
Bug report появился еще до зависания приложения, но нет кнопочки Save bug report, извратился картинкой в приложении, добавил кнопку, в следующий раз можно будет сохранить.
Application.ProcessMessages - все же используется в функции для работы с телнетом.
Ниже пример функции. У меня малость изменено, пока отлаживал подключение к телнет скидывал все данные в Memo,
и в этой функции вместо TelnetBuf - тот же Memo.
check for frozen main thread - не стояла, как зависнет запущу заново уже с этой галкой.
Присоединённый файл ( Кол-во скачиваний: 9 )
Безымянный.JPG 557,27 Kb
Репутация: нет
Всего: нет
Как то в этот раз довольно долго проработала.
В приложении репорт с "The application seems to be frozen"
Пожалуйста подскажите как выяснить причину.
Полгода уже, сил уже нет.
Присоединённый файл ( Кол-во скачиваний: 9 )
bugreport.txt 18,49 Kb
Репутация: 6
Всего: 72
Цитата |
00517b35 +c9d CheckBSC.exe Main 2047 +92 TForm1.SendData |
Это куда senddata? в клиентский сокет или одно из подключений серверного?
Репутация: нет
Всего: нет
Репутация: 6
Всего: 72
Имхо, именно на этом и возникает дедлок. Отправляй данные в контексте своего TServerThread и будет щасте.
Репутация: нет
Всего: нет
Вопрос - как в главном потоке вызвать эту процедуру?
С примерами туговато по этой части.
Репутация: 6
Всего: 72
Кто такой TServerThread? От чего он наследуется? Я правильно понял, что
За сим - умолкаю, так как с stThreadBlocking не работал, не работаю и не буду, а давать вредные советы - не хочу
Репутация: нет
Всего: нет
Буду продолжать поиски рабочих примеров.
Репутация: 6
Всего: 72
Пример, правда - в не-блокирующем режиме
Код заточен под работу из-под любого потока. Выложенная реализация, не лишена недостатков (в частности, в виде утечек памяти).
Смотреть метод TDataTransferServer.SendOutStreamToSingleConnection, он асинхронно (в нужном потоке) вызывает событие TDataTransferServer.OnWrite.
Репутация: нет
Всего: нет
Скорее всего надо функцию, которая будет приводить записи к String, а на стороне клиента обратно.
Репутация: 6
Всего: 72
ну а на клиенте - наоборот, только надо не забыть после считывания длины строки сделать ей SetLength
Репутация: нет
Всего: нет
Ошибка external exception c000001E
Сейчас изучаю TStream, раньше не приходилось с этим работать.
Репутация: 6
Всего: 72
1. объект Data не инициализирован
2. содержимое строки начинается с индекса [1]. Код для записи я привел.
Добавлено @ 12:24
ПыСы. Очень полезно читать, что Delphi пишет в Hints и Warnings при компиляции. В идеале - их вообще не должно быть.
Репутация: 2
Всего: 459
гениальность идеи состоит в том, что ее невозможно придумать
Репутация: нет
Всего: нет
TStream - абстрактный, необходимо создавать наследника.
Берем TMemoryStream, т.е. добавляем строку Data:= TMemoryStream.Create;?
Цитата |
2. содержимое строки начинается с индекса [1]. Код для записи я привел. |
Правда еще на практике не проверял, дома нет Delphi (т.е. исходников)
Репутация: 6
Всего: 72
Почитал справку про короткие строки (давненько не приходилось с ними работать) - код надо переписать.
Во-первых, короткие строки содержат по байту на символ, посему Length*SizeOf(Char) - не актуально и приведет к порче памяти, надо просто Length.
Во-вторых, короткие строки сразу получают в свое распоряжение столько памяти, сколько могут занять + 1 байт на длину, в нашем случае получится 11 байт. То есть - Length тоже не надо Правда и записывать их нужно с нулевого индекса, содержащего длину.
Итого получаем на передачу:
Добавлено через 1 минуту и 23 секунды
имелись ввиду юникодные версии Delphi
Добавлено через 2 минуты и 56 секунд
ЗЫ. И от такой красоты хотят избавиться, оставив в Delphi только один строковый тип.
Репутация: нет
Всего: нет
Спасибо за ответы.
Пересобрал сервер и клиент.
Сейчас поставил на тест.
Осталось прикрутить что то вроде onConnect и Client IP.
Репутация: 2
Всего: 459
Мне казалось, что тут как раз смысл стримов в том, чтобы снять необходимость в использовании строк фиксированной длинны. Т.е. если мы записываем поля по одному, а не всю запись целиком, то строка уже может быть указателем, ведь уже нет необходимости чтобы в памяти поля шли одно за другим непрерывно.
гениальность идеи состоит в том, что ее невозможно придумать
Репутация: 6
Всего: 72
В общем случае - абсолютно согласен. Но если поля достаточно мелкие по размеру и ограничения заданы высшими силами (к примеру - поля VARCHAR(10) в БД), то получается очень компактная и, имхо, красивая&понятная запись.
Добавлено через 3 минуты и 18 секунд
Хотя, да - если не знать структуру TData, да еще не помнить особенности ShortString (как, например, в моем случае) - то подобный код сразу вызывает опасения
Репутация: нет
Всего: нет
Ну думаю дальше тестировать нет смысла.
Уже понятно, что программа стабильно работает на протяжении почти недели.
Поэтому вопрос считаю решенным.
Но могу добавить:
- В интернете описан только один метод работы (один сервер)<->(много клиентов).
Может конечно плохо искал, но искал долго и упорно. Данный метод я реализовал в своей программе и пол года мучался.
- Метод предоставленный kami, отлично справляется со своей задачей.
(не знаю как насчет недостатков, но побочный эффект есть - если перезапустить сервер, то соединения не рвуться)
- Немного разобрался с потоками и TStream.
Всем ОГРОМНОЕ СПАСИБО.
Репутация: 6
Всего: 72
Вообще-то я давал код исключительно для того, чтобы кусок из него можно было адаптировать под stBlocking-режим, дабы не происходило дедлоков.
А какие хотелось бы? Клиент физически не в состоянии открыть несколько соединений, как бы этого ни хотелось. Можно создать много клиентов, положить их в один контейнер и обращаться с ними, как с группой. Но от этого "много клиентов, каждый из которых установил одно соединение" они ни во что другое не превратятся
Добавлено через 3 минуты и 11 секунд
1. Публиковать ссылки на вскрытые компоненты
2. Обсуждать взлом компонентов и делится вскрытыми компонентами
Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan.
[ Время генерации скрипта: 0.1896 ] [ Использовано запросов: 21 ] [ GZIP включён ]
Читайте также: