пятница, 17 апреля 2009 г.

Когда поток может принимать сообщения?

Это перевод When can a thread receive window messages? Автор: Реймонд Чен.

Каждый, кто игрался с оконными сообщениями, знают, что функции GetMessage и PeekMessage получают сообщения с очереди, которые распределяются (dispatch) окнам через DispatchMessage (прим. пер.: исправлен перевод "dispatch" с "отправки" на "распределение" по совету odysseos, старый вариант перевода был просто ужасен, т.к. вносит путаницу с "отправка"/"send" и "посылка"/"post", но другого варианта мне тогда в голову не пришло).

Большинство людей также знают, что GetMessage и PeekMessage также распределяют (dispatch) сообщения не из очереди (все ожидающие (pending) сообщения вне очереди отправляются, когда возвращается первое сообщение из очереди).

Но, видимо, не много людей осознают, что SendMessage также распределяет (dispatch) сообщения!

Если какой-то поток T1 посылает (send) сообщение окну, которое принадлежит другому потоку T2, отправляющий поток T1 засыпает, пока принимающий поток не ответит на сообщение. Но если кто-то ещё отправит (send) сообщение потоку T1, то поток T1 проснётся для обработки этого сообщения, а затем снова заснёт.

Почему так происходит?

Ну, когда два потока T1 и T2 работают вместе, нередко бывает так, что поток T1 может послать (send) сообщение потоку T2, а поток T2, в процессе обработки этого сообщения, может отправлять (send) сообщения потоку T1 до того, как он вернёт управление T1. Поэтому, поток T1 должен быть готов принять входящие отправленные (sent) сообщения.

Например, поток T1 может отправить (send) сообщение, говорящее: "Ну-ка, перечисли мне все X, которые ты знаешь". Тогда поток T2 отправит (send) сообщение потоку T1, говоря: "Вот мой X", а затем ещё одно: "А вот ещё один X", и т.д., пока он не перечислит все X. И только после этого он закончит обработку сообщения и вернёт управление.

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

Этот механизм вперёд-назад (back-and-forth) является основой для обнаружения службы DDE (DDE service discovery; прим. пер.: не понимаю в DDE, поэтому мог перевести неверно).

Другой пример: поток T1 отправляет сообщение потоку T2, а потоку T2 нужно спросить дополнительную информацию у потока T1, прежде чем вернуть ответ. Это не так странно, как может сперва показаться. Вы наверняка делали что-то похожее, хоть и не осознавали этого, когда вы отвечали на сообщение WM_NOTIFY отправкой сообщения элементу управления, который отправил исходное сообщение (например, вы можете ответить на LVN_ITEMACTIVATE ответной отправкой LVM_GETITEM для получении информации об элементе, который был активирован).

Поэтому помните: каждый раз, когда вы отправляете (send) сообщение, у вас появляется потенциальная возможность повторной входимости (re-entrancy).

Примечание переводчика: после изменения перевода слова dispatch, этот пост воспринимается полегче, но я всё равно решил оставить старый поясняющий пример:
// что-то делаем...
SendMessage(...); 
// что-то делаем ещё
Суть в том, что в вышеприведённом куске кода вы не можете гарантировать, что ваш поток выполнит строго написанные действия: что-то + SendMessage + ещё что-то. В момент, когда вы вызвали SendMessage и ждёте ответа, вам могут быть отправлены сообщения, которые будут обработаны внутри этого же вызова SendMessage! (речь идёт только об обратной отправке тоже через SendMessage, сообщение от PostMessage уйдёт в очередь). Иными словами, вышеприведённый код является в каком-то смысле аналогом такого кода (это очень грубая аналогия, поскольку Send <> Post):
// что-то делаем
PostMessage(...);
while not MessageProcessingCompleted do // пока сообщение, отправленное PostMessage, не обработано...
Application.ProcessMessages;          // обрабатываем входящие сообщения
// что-то делаем ещё

3 комментария:

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

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

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

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

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

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