как использовать postThreadMessage для передачи структуры

Я хочу использовать средства очереди сообщений Windows для отправки структуры другому потоку. Но я обнаружил, что функция postthreadmessage предоставляет мне только два целочисленных параметра lparam и wparam для передачи аргументов, поэтому я решил поместить адрес структуры в lparam. Это правильный способ, которым окна передают структуру?

И я намерен использовать boost :: shared_ptr для хранения адреса структуры как в потоке-получателе, так и в потоке-отправителе. Я сомневаюсь, что когда два shared_ptrs выйдут из области видимости, структура будет освобождена дважды? Я не могу придумать способ гарантировать, что структура, размещенная в куче, будет на 100% освобождена. Есть идеи?


person Jason    schedule 20.08.2011    source источник
comment
Да, конечно. Сравните WM_COPYDATA. Просто попросите ресивер освободить память. Не используйте PostThreadMessage (), если получающий поток отображает какие-либо окна, даже не окно сообщения.   -  person Hans Passant    schedule 20.08.2011
comment
@Hans Passant, старый пост, я знаю, но не могли бы вы вкратце объяснить, почему в этом случае плохо использовать PostThreadMessage ()? Какие могут быть последствия?   -  person Mark Ch    schedule 04.11.2015


Ответы (2)


Что касается первого вопроса, да, LPARAM предназначен для использования как целое число или указатель. Это ясно из определения:

typedef LONG_PTR LPARAM;

Это целое число, достаточное для размещения указателя.

Что касается shared_ptr, вы правы, если вы передадите необработанный указатель и оберните его в другой shared_ptr, вы освободите его дважды:

shared_ptr<Thing> a;
PostThreadMessage(x, 0, (LPARAM)a.get());
...
LRESULT OnMessage(int msg, WPARAM wp, LPARAM lp)
{
    shared_ptr<Thing> p((Thing*)lp); //Bad!!!
}

Но вместо этого вы можете попробовать этот обходной путь:

shared_ptr<Thing> a;
PostThreadMessage(x, 0, new shared_ptr<Thing>(a)); //pointer to smart-pointer
...
LRESULT OnMessage(int msg, WPARAM wp, LPARAM lp)
{
    shared_ptr<Thing> *pp = (shared_ptr<Thing>*)lp;
    shared_ptr<Thing> p(*pp);
    delete pp; //no leak
}

ПОСЛЕ ЗАДАЧИ: обратите внимание, что сообщение PostThreadMessage может завершиться ошибкой ... и вы не хотите, чтобы утечка shared_ptr.

По моему опыту, обычно лучше использовать std :: deque для хранения данных и использовать PostThreadMessage для уведомления о наличии данных. Таким образом, вы никогда не потеряете объект! YMMV

person rodrigo    schedule 20.08.2011
comment
да. но для самого использования общих данных требуется явная синхронизация потоков, и она будет заблокирована. Причина, по которой я использую сообщение, заключается в том, что я хочу асинхронную запись и не нуждаюсь в явной операции блокировки. Я думаю, что могу просто выделить память в потоке писателя и освободить его в потоке чтения, надеюсь, что PostThreadMessage никогда не потерпит неудачу ... - person Jason; 20.08.2011
comment
Комментарий к запоздалой мысли: как насчет использования указателя на weak_ptr. Утечка weak_ptr намного лучше, чем утечка shared_ptr, и пока отправитель сообщения держится за shared_ptr a, объект будет существовать, когда он понадобится получателю. - person Rasmus Storjohann; 20.08.2011
comment
Я видел сбой PostThreadMessage только в двух случаях: а) целевая очередь заполнена (я насчитал около 40000 сообщений, но лимит не задокументирован, поэтому он может зависеть от многих параметров); и б) целевой поток еще не создал очередь сообщений. Очередь создается автоматически, когда поток в первый раз использует функцию для доступа к ней (например, GetMessage). Так что, если вы создадите поток и сразу после этого вызова PostThreadMessage он может потерпеть неудачу. На всякий случай просто проверьте возвращаемое значение: PostThreadMessage возвращает FALSE в случае неудачи. - person rodrigo; 22.08.2011

Я столкнулся с аналогичной ситуацией с использованием класса Qt QModelIndex, который хранит данные с помощью void*, но я управляю данными, на которые он указывает, с помощью shared_ptr.

Чтобы избежать прямого разыменования void*, который может указывать на уже не существующий объект, я использую карту от void* до weak_ptr. Все объекты, на которые ссылается QModelIndexes, хранятся на карте. Когда приходит время разыменовать void*, я использую void* как ключ для извлечения соответствующего weak_ptr с карты, а затем конвертирую weak_ptr в shared_ptr. Тогда shared_ptr либо равно void*, либо NULL, и вы также получаете безопасность типов.

Обратите внимание, что мое решение не касалось упомянутых вами проблем параллелизма, но вы можете адаптировать его к своей ситуации. Вы можете использовать lparam для хранения необработанного указателя на ваш объект, а затем использовать карту для преобразования необработанного указателя в интеллектуальный. Защитите карту мьютексом, и вы можете быть там.

person Rasmus Storjohann    schedule 20.08.2011