Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Проблема с TFileStream


Страницы:   1 2 3 4 5

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] исчерпана (до очередного неофита, наступившего туда же). Так не лучше ли завести другую ветку и в другом месте? Например, "Прочее".


Страницы:   1 2 3 4 5

Наверх

Титульная страница Поиск, карта сайта Написать письмо