суббота, 6 февраля 2010 г.

PulseEvent имеет фундаментальный изъян

Это перевод PulseEvent is fundamentally flawed. Автор: Реймонд Чен.

Функция PulseEvent освобождает один поток (или все потоки, если вы используете событие с ручным сбросом), который ждал событие, после чего возвращает событие в сброшенное (unset) состояние. Если ни один поток не ждал событие, то ничего не происходит.

И в этом-то и есть проблема.

Как вы узнаете, ждёт ли поток, на самом деле, событие, как вы думаете? Конечно же, вы не можете сделать так:
SignalSemaphore(hOtherSemaphore);
WaitForSingleObject(hEvent, INFINITE);
потому что в этом примере есть гонка (race) между установкой семафора и ожиданием. Поток, который пробуждается семафором, может выполнить всю свою работу и вызвать PulseEvent на событие прежде, чем этот код дойдёт до WaitForSingleObject.

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

Дело в том, что пока поток ждёт события, драйвер устройства или даже часть самого ядра могут "угнать" (borrow) поток для выполнения какой-нибудь задачи (посредством "kernel-mode APC"). В течение этого времени поток не находится в режиме ожидания (поскольку он занят выполнением работы драйвера устройства). И если вызов PulseEvent произойдёт в тот момент, когда поток "позаимствовали", то он не проснётся, поскольку функция PulseEvent пробуждает только потоки, которые ждали в момент вызова функции.

Дело даже не в том, что вы (как прикладная программа) не можете запретить ядру брать ваш поток, а в том, что вы вообще не можете определить, что это случилось.

(Одним из мест, где вы, вероятно, увидите это в действии, является подключение отладчика к процессу, поскольку отладчик делает такие вещи как приостановка и возобновление потоков, которые приводят к выполнению APC ядра).

В результате - функция PulseEvent является бесполезной и её следует избегать. Она существует исключительно ради обратной совместимости.

Примечание: эти все вещи с APC ядра также означают, что вы не можете предсказать, какой поток будет разбужен, когда вы включаете семафор, событие с авто-сбросом или любой другой объект синхронизации, который в сигнальном состоянии освобождает только один поток. Если поток "позаимствован" для обслуживания APC ядра, то когда он возвращается в очередь ожидания, он становится в её конец. Соответственно, порядок объектов, ожидающих объект ядра, непредсказуем, и вы не должны делать никаких предположений о нём.

1 комментарий:

  1. В самом деле, при отладке не всегда объект получает уведомление от PulseEvent().

    ОтветитьУдалить

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

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

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

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

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

Примечание. Отправлять комментарии могут только участники этого блога.