Sign in Join
Wiki copytemporary

Avoiding implicit try finally section/ru: what changed

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

Title: Avoiding implicit try finally section/ru

OldNewLine
1+## Обзор
2+
3+Иногда полезно знать, что компилятор может обернуть код в неявный try ... finally блок. По сути дела это необходимо, когда вы используете переменную любого типа, которая должна быть инициализирована / деинициализирована (освобождена). Иными словами, стандартные процедуры Initialize() и Finalize() должны что-то сделать с ней. Это может быть актуально, например, при использовании переменных стандартных типов подобных AnsiStrings, Variants или динамических массивов.
4+
5+Для примера, рассмотрим следующею процедуру:
6+
7+``` pascal
8+procedure P;
9+var
10+ S: AnsiString;
11+begin
12+ ... делаем что-то с S ...
13+end;
14+```
15+
16+Данная процедура, фактически компилируется как:
17+
18+``` pascal
19+procedure P;
20+var
21+ S: AnsiString;
22+begin
23+ Initialize(S);
24+ try
25+ ... делаем что-то с S ...
26+ finally Finalize(S) end;
27+end;
28+```
29+
30+Это необходимо, чтобы быть уверенным, что счетчик ссылок на переменную S, будет правильным, если процедура P завершится с ошибками. Однако, в некоторых случаях это может существенно повлиять на скорость данного кода.
31+
32+Единственный правильный способ узнать, что происходит – просмотр вывода кода ассемблер.
33+
34+Более подробную информацию, можете найти здесь: <http://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg01367.html>
35+
36+## Возможные решения
37+
38+- используйте директиву компилятора {\$implicitexceptions off}, но только в окончательных версиях программы, представляющих собой готовый программный продукт. Отладка программы с этой директивой, может стать проблемной, из-за особенностей обнаружения утечек памяти и её повреждения.
39+
40+- разбить редко использующийся код в катером находится не явный блок try…finally на отдельные процедуры. (Вы можете использовать процедуры внутри других процедур)
41+
42+- использовать параметры-константы, а не параметры-значение (ключевое слово const при объявлении параметров). Это исключает необходимость менять счетчик ссылок, но временные переменные внутри процедур все еще могут быть проблемой.
43+
44+- использовать глобальные переменные. Однако, в данном вопросе, следует проявить осторожность.
45+
46+- использовать типы переменных, не зависящих от счётчика ссылок, например shortstring.
47+
48+## Возможные риски
49+
50+> **Warning:** Используйте осторожно вызов данных исключений. Если вы оставите их без обработки, то возможны утечки памяти.
51+
52+В 2007 году, директива \$implicitexceptions была добавлена к модулю strutils. Если вы используете данный модуль, то обратите внимание на следующие нюансы:
53+
54+- Процедура, использующая другую процедуру, в которой вызывается исключение не безопасна. Например – strtoint, но не strtointdef.
55+- Процедура, которая вызывает исключения, сама по себе небезопасна.
56+- Не используйте очень большие процедуры из-за риска ошибок и низкой производительности их кода. Например, форматирование даты и времени.
57+- Использование чисел с плавающей запятой может вызвать исключения, которые будут перехвачены исключениями модуля sysutils.
58+
59+Если вы обнаружили проблемы с этими изменениями, пожалуйста, свяжитесь с Marco.
60+
61+## Пример программы
62+
63+Небольшая демонстрация программа, которая:
64+
65+- При запуске показывает, что отсутствие неявного try ... finally блока, может сделать код намного быстрее. Результаты её тестирования:
66+
67+`Время Foo_Normal: 141`\
68+`Время Foo_Faster: 17`
69+
70+- Показывает использование неявного блока try ... finally (без изменения сути или безопасности кода) в некоторых случаях (когда вам не нужно использовать AnsiString AnsiString/Variant/и т.п. каждый раз, когда процедура вызывается, а, например, только если параметр не имеет определенного значения).
71+
72+``` pascal
73+{$mode objfpc}{$H+}
74+
75+uses
76+ {BaseUnix, Unix нужны только для реализации функции Clock} BaseUnix, Unix,
77+ SysUtils;
78+
79+function Clock: Int64;
80+var Dummy: tms;
81+begin
82+ Clock := FpTimes(Dummy);
83+end;
84+
85+procedure Foo_Normal(i: Integer);
86+var S: string;
87+begin
88+ if i = -1 then
89+ begin
90+ S := 'Некоторые операции с AnsiString';
91+ raise Exception.Create(S);
92+ end;
93+end;
94+
95+procedure Foo_Faster(i: Integer);
96+
97+ procedure RaiseError;
98+ var S: string;
99+ begin
100+ S := 'Некоторые операции с AnsiString';
101+ raise Exception.Create(S);
102+ end;
103+
104+begin
105+ if i = -1 then RaiseError;
106+end;
107+
108+{ Обратите внимание, что, когда я называю Foo_Normal и Foo_ResourceString
109+ i всегда >= 0 так что исключения никогда не происходит.
110+ Поэтому строковые константы SNormal и SResString не используется. }
111+
112+const
113+ TestCount = 10000000;
114+var
115+ i: Integer;
116+ Start: Int64;
117+begin
118+ Start := Clock;
119+ for i := 0 to TestCount do Foo_Normal(i);
120+ Writeln('Время Foo_Normal: ', Clock - Start);
121+
122+ Start := Clock;
123+ for i := 0 to TestCount do Foo_Faster(i);
124+ Writeln('Время Foo_Faster: ', Clock - Start);
125+end.
126+```
127+
128+---
129+*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