![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблема с TFileStream
ProgRAMmer Dimonych © (2007-11-05 22:45:00)Сталкивался много раз, сейчас решил разобраться :)
Дело в том, что TFileStream как-то странно себя ведёт. Даже, я бы сказал, непредсказуемо, т.е. не так, как должен был бы.
Например, в программе, которую я пишу сейчас используется вспомогательный модуль, содержащий объект для работы с файлами определённого формата. В некоторых его методах используется локальный
Tmp:TFileStream;
Забавно то, что в начале процедуры этот самый Tmp показывается (в Delphi'овом Hint'е) со свойством (Handle:12) и таким же остаётся после вызова его метода Free. То же самое с FileStream'ом, который блокирует файл до тех пор, пока он нужен моему объекту, и который объявлен как поле объекта.
Самое печальное, что, похоже, именно из-за такого поведения возникают AV и External Errors. Посмотрел исходник TFileStream'а и справку по Дельфям: в деструкторе TFileStream'а вызывается функция FileClose, которая, если верить справке, просто принимает Handle файла, что, на мой взгляд, имеет печальные последствия:
destructor TFileStream.Destroy;
begin
if FHandle >= 0 then FileClose(FHandle);
inherited Destroy;
end;
Т.е. FHandle передаётся не как var-параметр, аinherited Destroyтянется аж от TObject, где он virtual.
Попробовал копнуть в сети, но русскоязычного ничего не нашёл. Насколько мне позволяет судить моё знание английского, что-то по этой проблеме есть здесь: http://www.delphipraxis.net/topic77280.html.Если я правильно понимаю немецкий, то вместо метода Free следует юзать процедуру FreeAndNil().
Это так?
Юрий Зотов (2007-11-06 11:00:00) [21]> homm © (06.11.07 10:40) [67]
Тут ничего нового, так оно в той же джаве, например, и работает. Плюс там никаких Free вообще нет, а счетчик ссылок рулится их явных присвоением, их явной очисткой и областями их видимости (как длинные строки и динамические массивы в Delphi).
Ну да ладно, вернемся к Delphi (или KOL). Пусть работает подсчет ссылок и мы имеем вот что:
type
TMyObject = ...
FRef: TObject;
procedure Method1;
procedure Method2;
end;
procedure TMyObject.Method1;
begin
FRef := TSomeObject.Create(...);
... // Что-то делаем
if SomeCondition then
FRef.Free;
end;
procedure TMyObject.Method2;
begin
... // Вопрос на засыпку - как мне в этом месте кода узнать,
... // существует ли объект, на который ссылается поле FRef,
... // или он уже уничтожен? Конечно, учитывая, что в других
... // местах программы тоже могут ссылки на этот же объект
end;
vpbar (2007-11-06 10:57:00) [22]>>Юрий Зотов © (06.11.07 10:10) [63]
> Ой ли?
- так вы про скорость работы?
А я понял из этого
> Вы представляете себе, что это будет за компилятор? Сложность
> его? Скорость его?
что про скорость компилятора. Скорость компилятора не сильно изменится должна. Ведь при присваивании строк тоже не просто мувом обходятся. И ничего.
А вот скорость присваивания выростет - это да. Но тут уже вопрос оптимизации.
>>Сколько угодно. Максимально - сколько хватит памяти
Не спорю. Но с трудом представляю зачем на каждый объект ссылаться из сотни мест сразу.
> как поступать разработчикам Вашего компилятора?
Примерно так же как поступили разработчики TList. Посмотрите его реализацию. Память там перераспределяетя не на каждый чих.
>
> >Так что скажем 320 бит в среднем на объект хватит
> Размер объекта TFont - 40 байт. При Вашем подходе он становится
> 360 байт. Разница в 9 раз. Примерно такая же картина и по
> другим объектам. Итого можно прогнозировать увеличение расхода
> памяти в 5-10 раз.
>
Эээ. откуда лишние 360-40 = 320 Байт?? Я предлагал 320 Битт.е. 40 Байт. Да в два раза больше. Но опять же можно оптимизировать.
> > и перераспределять необязательно.
Пока в пределах 32х ссылок не надо перераспределять. Для 33 выделить очередной блок памяти
homm (2007-11-06 10:40:00) [23]> [66] Юрий Зотов © (06.11.07 10:33)
Я говорю о подсчете ссылок как таковом, не с точки зрания сабжа. Посмотрите, на пример, там [64] Вы сами рулите кодом. После того, как Вы вызвали MyBitmap.Free; для вас он уже уничтожен (как бы ни было на самом деле) и пользоваться им не должны. А для компонента TComponent он существует, и если бы не вызывать MyBitmap.Free; он будет существовать для обоих кусков кода, помоему все просто и логично, и не нужно беспокоиться о том, что кто-то уничтожит объект. Нужен на каком-то участке — добавил ссылку, не нужен больше — Free, и пофиг, уничтожился ли объект на самом деле, главное что твоей ссылки на него больше нет, как только других ссылок тоже не окажется, уничтожится.
Юрий Зотов (2007-11-06 10:33:00) [24]> homm © (06.11.07 10:20) [64]
1. Верно, спутал. Всего лишь вдвое, оказывается. :o)
2. Дык... а что дает подсчет ссылок в плане сабжа? Переменные, который их хранят - они ведь все равно сами собой не очистятся. И даже еще хуже получится, потому что станет вообще невозможно определить, существует ли еще объект, или уже уничтожен, хоть обнуляй переменные вручную, хоть не обнуляй.
Поэтому насчет удобства: кому как, а мне удобнее другое - когда я сам рулю программой так, как мне это надо, и никто мне не мешает это делать.
homm (2007-11-06 10:21:00) [25]> fBitmap.RefInc;
if fBitmap <> nil
fBitmap.RefInc;
homm (2007-11-06 10:20:00) [26]> [63] Юрий Зотов © (06.11.07 10:10)
> >Так что скажем 320 бит в среднем на объект хватит
> Размер объекта TFont - 40 байт. При Вашем подходе он становится
> 360 байт.
При его подходе он становится 80 байтили 640 бит. Хотя конечно ы целом с вами сагласен, нафиг это не нужно.
С другой стороны удивляет, почему не был опрганизован элементврный подсчет ссылок, как в KOL. Это же очень удобно.
MyBitmap := TBitmap.Create;
Component1.Bitmap := MyBitmap;
MyBitmap.Free;
function TComponent.SetBitmap(aBitmap: TBitmap);
begin
fBitmap.Free;
fBitmap := aBitmap;
fBitmap.RefInc;
end;
function TComponent.Destroy();
begin
fBitmap.Free;
end;
Юрий Зотов (2007-11-06 10:10:00) [27]> vpbar © (06.11.07 09:43) [58]
> Скорость не сильно пострадает.
Ой ли?
Пусть имеем a := b; // Обе переменные - ссылки на объект.
Если не учитывать ссылки, то это одна команда MOVE. А если учитывать, то к этой команде надо еще добавить внесение новой ссылки в список ссылок в самом объекте. А если емкость списка исчерпана, то еще и перераспределить память под него.
И так почти при каждом чихе, потому что операция ОЧЕНЬ частая. Итого можно прогнозировать замедление на один-два порядка. То есть, от 10 до 100 раз.
> Надеженость, конечно зависит от сложности, но не так прямо.
В основном, от сложности и зависит. А от чего ей еще зависеть? Простые вещи разработчиками компиляторов давно отработаны.
> А сколько может быть максимально ссылок?
Сколько угодно. Максимально - сколько хватит памяти.
> Ну штук десяток. Больше редко.
И как поступать разработчикам Вашего компилятора? Сколько памяти отводить под список ссылок? Отвести N байт и надеяться на "авось, больше не потребуется"? Глупо. Менять емкость списка динамически? Тогда на каждом чихе пойдет перераспределение памяти - а это серьезные тормоза и серьезная фрагментация памяти.
>Так что скажем 320 бит в среднем на объект хватит
Размер объекта TFont - 40 байт. При Вашем подходе он становится 360 байт. Разница в 9 раз. Примерно такая же картина и по другим объектам. Итого можно прогнозировать увеличение расхода памяти в 5-10 раз.
> и перераспределять необязательно.
Вот это мило. А как? Отвести под список 320 байт и молиться Богу, чтобы не появилась 11-я ссылка? Класная программа получится.
homm (2007-11-06 09:59:00) [28]> [61] vpbar © (06.11.07 09:54)
> Я не увидел в /34/ ничего такого.
Сори, в [29].
Ну тогда ссылки считать, и не освобождать пока все ссылки не обнулятся.
vpbar (2007-11-06 09:54:00) [29]
> homm © (06.11.07 09:43) [59]
Вы о чем? Я не увидел в /34/ ничего такого.
В КОЛ просто делается в free
if @ Self <> nil then RefDec;
Да объект не удалится пока fRefCount>0
Но увеличение ссылок возложено на программиста, ему нужно RefInc самому вызывать. Или вы какойто другой механизм в КОЛ имеете ввиду?
vpbar (2007-11-06 09:44:00) [30]>>Юрий Зотов © (06.11.07 09:42) [57]
Нда. Надо сохранить ветку как доказательство моих авторских прав на идею :)
homm (2007-11-06 09:43:00) [31]> [56] vpbar © (06.11.07 09:33)
> В KOL не совсем так.
Не совсем так как в COM, но так, как в [34] написано. О добавлении ссылок, конечно сам программист должен заботится.
vpbar (2007-11-06 09:43:00) [32]
> >>Юрий Зотов © (06.11.07 01:14) [47]
>
> > Вы представляете себе, что это будет за компилятор? Сложность
>
> > его? Скорость его? Надежность его?
Да. Не сложнее существующих. Такой же простой как, например компилятор Делфи. Скорость не сильно пострадает. Надеженость, конечно зависит от сложности, но не так прямо.
> > И объект, хранящий адреса всех ссылок на себя - Вы представляете,
>
> > насколько увеличится объем занимаемой им памяти? Понимаете,
>
> > что память эту придется еще и постоянно перераспределять
>
> > (количество ссылок ведь меняется)? Что это будут серьезные
>
> > тормоза, что увеличится фрагментированность памяти?
Представляю. А сколько может быть максимально ссылок? Ну штук десяток. Больше редко. Так что скажем 320 бит в среднем на объект хватит и перераспределять необязательно. На 64х разрядной платформе поболее будет, но там все побольше.
> > И главное - а зачем ве это?
В данной ветке, просто как пример что альтернатива может быть. А вообще действительно это мало кому нужно. Ибо пошли другим путем. Ссылки считают.
Юрий Зотов (2007-11-06 09:42:00) [33]> vpbar © (06.11.07 09:33) [56]
Подсчет ссылок на объект сделать, кстати, можно. И сделано (в той же джаве, скажем). И в Delphi это есть (интерфейсная модель).
Но счетчик ссылок позволит лишь узнать, есть они или нет. И по-прежнему не позволит их обнулить при разрушении объекта. Поскольку адреса самих ссылок неизвестны.
vpbar (2007-11-06 09:33:00) [34]>>homm © (06.11.07 07:01) [54]
Неее. В KOL не совсем так. Ну не мог Кладов исправить компилятор и заставить вести его подчет ссылок на объект.
Anatoly Podgoretsky (2007-11-06 07:51:00) [35]> Юрий Зотов (06.11.2007 00:24:36) [36]
Тем более если ее не существует.
homm (2007-11-06 07:01:00) [36]> [34] ProgRAMmer Dimonych © (06.11.07 00:14)
> > Ну тогда ссылки считать, и не освобождать пока все ссылки
> > не обнулятся.
>
> В COM, кажется, так делается?
В КОЛ тоже так-же. Чесно говоря всю жизнь думал, что и в VCL так-же. Счас поискал подобное, не нашел. Кашмар… Может плохо искал?
homm (2007-11-06 06:50:00) [37]> [19] ProgRAMmer Dimonych © (05.11.07 23:47)
> Кажется, понял. Тогда получается, что, действительно, без
> присвоения указателю FFile:=nil будет глючить. Так вроде?
Не так. Будет глючить, если ты плохой программист, и логика твоей программы тупая. Иногда действительно логику работы строят на определении доступности какого-то объекта во время выполнения. В этом случае уместно определять эту самую доступность по равенству указателя нулю. Тогда и нужно обнулять переменную. Но в большенствеслучаев рограммисту заранее известно какой объект на каком участке кода доступен, и к чему можно обратиться.
И это, TFileStream вообще не причем. ([2])
korneley (2007-11-06 03:12:00) [38]
> Но, имхо, кроме тебя в сей ветке этот вопрос никого не
> интересует
Дык, понятно, четвертый час пошел... :)
Германн (2007-11-06 03:07:00) [39]
> korneley © (06.11.07 02:58) [50]
>
>
> > Люди все разные, кому-то быть художником, кому - программистом,
>
> > кому - певцом
> Да, конечно. Но тема [0] исчерпана (до очередного неофита,
> наступившего туда же). Так не лучше ли завести другую ветку
> и в другом месте? Например, "Прочее".
>
Ну так заведи, если тебе это интересно. Возражать никто не будет. Но, имхо, кроме тебя в сей ветке этот вопрос никого не интересует.
korneley (2007-11-06 02:58:00) [40]
> Люди все разные, кому-то быть художником, кому - программистом,
> кому - певцом
Да, конечно. Но тема [0] исчерпана (до очередного неофита, наступившего туда же). Так не лучше ли завести другую ветку и в другом месте? Например, "Прочее".