пятница, 25 декабря 2009 г.

Нужно ли вам закрывать описатели "одноразовых" таймеров?

Это перевод Do you need clean up one-shot timers? Автор: Реймонд Чен.

Функция CreateTimerQueueTimer позволяет вам создавать "одноразовый" таймер, передавая флаг WT_EXECUTEONLYONCE. Документация говорит, что вам нужно вызвать функцию DeleteTimerQueueTimer, когда вам больше не нужен таймер.

Но почему вам нужно закрывать одноразовые таймеры?

Чтобы ответить на это, я хотел бы представить вам один из моих любимых риторических вопросов, которые я использую при решении головоломок с API: "на что был бы похож мир, если бы это было так?".

Представьте, на что был бы похож мир, если бы вы могли не закрывать описатели одноразовых таймеров.

Ну, для начала, это означает, что поведение функции может быть запутанным. Вызывающий функцию CreateTimerQueueTimer теперь должен отслеживать, какие таймеры были одноразовыми, а такие - нет, чтобы знать, надо ли ему закрывать описатели или нет.

Но намного, намного более плохой вещью будут авто-закрывающиеся таймеры, потому что правильно использовать их будет практически невозможно.

Предположим, что у вас есть объект, который создаёт одноразовый таймер, и вы хотите удалить таймер в деструкторе, если он не успел сработать. Если бы одноразовые таймеры были бы самоудаляющимися, то вы не смогли бы написать такой объект.
type
TSample = class
private
m_hTimer: THandle;
// ...
public
constructor Create;
destructor Destroy; override;
end;

constructor TSample.Create;
begin
inherited;

CreateTimerQueueTimer(m_hTimer, ...);
end;

destructor TSample.Destroy;
begin
// что написать тут?
inherited;
end;
Вы можете сказать: ну я могу обнулить поле m_hTimer в callback-ке таймера. Таким образом, деструктор будет знать, когда таймер сработал".

Только теперь у вас условие гонки.
class procedure TSample.Callback(const Context: Pointer); stdcall; static;
begin
// ОКНО ДЛЯ ГОНКИ:
TSample(Context).m_hTimer := 0;
// ...
end;
Если callback будет вытеснен во время выполнения окна (до обнуления поля), и тут же запустится деструктор объекта, то объект попытается использовать неверный описатель, поскольку таймер уже закрыт.

Это условие гонки нельзя обойти, потому что гонка происходит до того, как хотя бы одна инструкция вашего кода получит шанс на выполнение.

Так что будьте благодарны, что вам нужно закрывать описатели одноразовых таймеров.

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.