Безопасно ли передавать (синхронно) выделенную в стеке память другому потоку? - PullRequest
4 голосов
/ 11 февраля 2010

Недавно я слышал, что память в стеке не используется совместно с другим потоком, а память в куче - совместно с другими потоками.

Я обычно делаю:

HWND otherThreadHwnd;
DWORD commandId;
// initialize commandId and otherThreadHwnd

struct MyData {
  int data1_;
  long data2_;
  void* chunk_;
};

int abc() {
  MyData myData;
  // initialize myData
  SendMessage(otherThreadHwnd,commandId,&myData);
  // read myData
}

Это нормально?

Ответы [ 5 ]

3 голосов
/ 11 февраля 2010

Да, в этом случае это безопасно.

Данные в стеке существуют только для времени жизни вызова функции. Поскольку SendMessage является синхронным блокирующим вызовом, данные будут действительны в течение всего этого вызова.

Этот код будет нарушен, если вы замените SendMessage вызовом PostMessage, SendNotifyMessage или SendMessageCallback, поскольку они не будут блокироваться, и функция может вернуться до того, как целевое окно получит сообщение.

2 голосов
/ 11 февраля 2010

Я думаю, что те, кто «слышал, что память в стеке не используется совместно с другим потоком», путают две разные проблемы:

  1. время жизни объекта - данные в стеке действительны только до тех пор, пока поток не покидает область действия имени переменной. В приведенном вами примере вы обрабатываете это, синхронно вызывая другой поток.

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

2 голосов
/ 11 февраля 2010

Да, все в порядке.

SendMessage работает в режиме блокировки. Даже если myData выделено в стеке, его адрес все еще виден всем потокам в процессе. Каждый поток имеет свой собственный стек; но данные в стеке могут быть явно использованы, например, вашим кодом. Однако, как вы уже догадались, не используйте PostThreadMessage в таком случае.

0 голосов
/ 15 сентября 2011

Как уже говорили другие, то, как вы это написали, просто отлично, и, в общем, ничего не произойдет немедленно при передаче указателя на объект в стеке в другой поток, если все синхронизировано.Тем не менее, я склонен немного напрягаться при этом, потому что вещи, которые кажутся потокобезопасными, могут выйти из своего запланированного порядка, когда происходит исключение или если один из потоков связан с асинхронными обратными вызовами ввода-вывода.В случае возникновения исключения в другом потоке во время вашего вызова SendMessage, он может немедленно вернуть 0.Если исключение позднее обрабатывается в другом потоке, у вас может быть нарушение прав доступа.Еще одна потенциальная опасность заключается в том, что все, что хранится в стеке, никогда не может быть принудительно удалено из другого потока.Если он застрянет в ожидании какого-то обратного вызова, объекта и т. Д. Навсегда, и пользователь решил отменить или закрыть приложение, у рабочего потока не будет возможности убедиться, что остановленный поток убрал все объекты в своем стеке.

Моя точка зрения такова: в простых сценариях, которые вы описали, где все работает идеально, ничего не меняется и не происходит сбой внешних зависимостей, совместное использование указателей на локальный стек безопасно, но поскольку распределение в куче являетсяна самом деле все так же просто, и это дает вам возможность явно контролировать время жизни объекта из любого потока в смягчающих обстоятельствах, почему бы просто не использовать кучу?

Наконец, я настоятельно рекомендую вам быть очень осторожным с элементом void * chunk_ вашей структуры MyData, поскольку он не является потокобезопасным, как описано, если он копируется в другой поток.

0 голосов
/ 02 июля 2010

То, о чем вы слышали, это «потенциальное нарушение конфиденциальности», когда данные из частного стека одного потока делятся с другим потоком.

Хотя это не поощряется, это всего лишь «потенциальная» проблема - при правильной синхронизации это можно сделать безопасно. В вашем случае эта синхронизация выполняется с помощью :: SendMessage (); оно не вернется, пока сообщение не будет обработано в другом потоке, поэтому данные не будут выходить за пределы области видимости в стеке основного потока. Но имейте в виду, что что бы вы ни делали с этим указателем в рабочем потоке, это нужно сделать перед возвратом из обработчика сообщений (если вы храните его где-то, обязательно сделайте копию).

...