Sign in Join
Wiki copytemporary

Avoiding implicit try finally section/ru: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Обзор
22
33Иногда полезно знать, что компилятор может обернуть код в неявный try ... finally блок. По сути дела это необходимо, когда вы используете переменную любого типа, которая должна быть инициализирована / деинициализирована (освобождена). Иными словами, стандартные процедуры Initialize() и Finalize() должны что-то сделать с ней. Это может быть актуально, например, при использовании переменных стандартных типов подобных AnsiStrings, Variants или динамических массивов.
44
55Для примера, рассмотрим следующею процедуру:
66
77``` pascal
88procedure P;
99var
1010 S: AnsiString;
1111begin
1212 ... делаем что-то с S ...
1313end;
1414```
1515
1616Данная процедура, фактически компилируется как:
1717
1818``` pascal
1919procedure P;
2020var
2121 S: AnsiString;
2222begin
2323 Initialize(S);
2424 try
2525 ... делаем что-то с S ...
2626 finally Finalize(S) end;
2727end;
2828```
2929
3030Это необходимо, чтобы быть уверенным, что счетчик ссылок на переменную S, будет правильным, если процедура P завершится с ошибками. Однако, в некоторых случаях это может существенно повлиять на скорость данного кода.
3131
3232Единственный правильный способ узнать, что происходит – просмотр вывода кода ассемблер.
3333
3434Более подробную информацию, можете найти здесь: <http://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg01367.html>
3535
3636## Возможные решения
3737
3838- используйте директиву компилятора {\$implicitexceptions off}, но только в окончательных версиях программы, представляющих собой готовый программный продукт. Отладка программы с этой директивой, может стать проблемной, из-за особенностей обнаружения утечек памяти и её повреждения.
3939
4040- разбить редко использующийся код в катером находится не явный блок try…finally на отдельные процедуры. (Вы можете использовать процедуры внутри других процедур)
4141
4242- использовать параметры-константы, а не параметры-значение (ключевое слово const при объявлении параметров). Это исключает необходимость менять счетчик ссылок, но временные переменные внутри процедур все еще могут быть проблемой.
4343
4444- использовать глобальные переменные. Однако, в данном вопросе, следует проявить осторожность.
4545
4646- использовать типы переменных, не зависящих от счётчика ссылок, например shortstring.
4747
4848## Возможные риски
4949
5050> **Warning:** Используйте осторожно вызов данных исключений. Если вы оставите их без обработки, то возможны утечки памяти.
5151
5252В 2007 году, директива \$implicitexceptions была добавлена к модулю strutils. Если вы используете данный модуль, то обратите внимание на следующие нюансы:
5353
5454- Процедура, использующая другую процедуру, в которой вызывается исключение не безопасна. Например – strtoint, но не strtointdef.
5555- Процедура, которая вызывает исключения, сама по себе небезопасна.
5656- Не используйте очень большие процедуры из-за риска ошибок и низкой производительности их кода. Например, форматирование даты и времени.
5757- Использование чисел с плавающей запятой может вызвать исключения, которые будут перехвачены исключениями модуля sysutils.
5858
5959Если вы обнаружили проблемы с этими изменениями, пожалуйста, свяжитесь с Marco.
6060
6161## Пример программы
6262
6363Небольшая демонстрация программа, которая:
6464
6565- При запуске показывает, что отсутствие неявного try ... finally блока, может сделать код намного быстрее. Результаты её тестирования:
6666
67-`Время Foo_Normal: 141`\
68-`Время Foo_Faster: 17`
67+```
68+Время Foo_Normal: 141
69+Время Foo_Faster: 17
70+```
6971
7072- Показывает использование неявного блока try ... finally (без изменения сути или безопасности кода) в некоторых случаях (когда вам не нужно использовать AnsiString AnsiString/Variant/и т.п. каждый раз, когда процедура вызывается, а, например, только если параметр не имеет определенного значения).
7173
7274``` pascal
7375{$mode objfpc}{$H+}
7476
7577uses
7678 {BaseUnix, Unix нужны только для реализации функции Clock} BaseUnix, Unix,
7779 SysUtils;
7880
7981function Clock: Int64;
8082var Dummy: tms;
8183begin
8284 Clock := FpTimes(Dummy);
8385end;
8486
8587procedure Foo_Normal(i: Integer);
8688var S: string;
8789begin
8890 if i = -1 then
8991 begin
9092 S := 'Некоторые операции с AnsiString';
9193 raise Exception.Create(S);
9294 end;
9395end;
9496
9597procedure Foo_Faster(i: Integer);
9698
9799 procedure RaiseError;
98100 var S: string;
99101 begin
100102 S := 'Некоторые операции с AnsiString';
101103 raise Exception.Create(S);
102104 end;
103105
104106begin
105107 if i = -1 then RaiseError;
106108end;
107109
108110{ Обратите внимание, что, когда я называю Foo_Normal и Foo_ResourceString
109111 i всегда >= 0 так что исключения никогда не происходит.
110112 Поэтому строковые константы SNormal и SResString не используется. }
111113
112114const
113115 TestCount = 10000000;
114116var
115117 i: Integer;
116118 Start: Int64;
117119begin
118120 Start := Clock;
119121 for i := 0 to TestCount do Foo_Normal(i);
120122 Writeln('Время Foo_Normal: ', Clock - Start);
121123
122124 Start := Clock;
123125 for i := 0 to TestCount do Foo_Faster(i);
124126 Writeln('Время Foo_Faster: ', Clock - Start);
125127end.
126128```
127129
128130---
129131*From the Free Pascal wiki, page "Avoiding implicit try finally section/ru", as it stood on 2020-02-09 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history