visual Должны ли данные, передаваемые в поток, быть изменчивыми?

В Microsoft Visual C ++ я могу вызвать CreateThread (), чтобы создать поток, запустив функцию с одним void * параметр. Я передаю указатель на структуру в качестве этого параметра, и я вижу, что многие другие люди также делают это.

Мой вопрос: если я передаю указатель на мою структуру, как я узнаю, были ли члены структуры действительно записаны в память до вызова CreateThread ()? Есть ли гарантия, что они не будут просто кэшированы? Например:

struct bigapple { string color; int count; } apple;
apple.count = 1;
apple.color = "red";
hThread = CreateThread( NULL, 0, myfunction, &apple, 0, NULL );

DWORD WINAPI myfunction( void *param )
{
struct bigapple *myapple = (struct bigapple *)param;

// how do I know that apple's struct was actually written to memory before CreateThread?
cout << "Apple count: " << myapple->count << endl;
}

Сегодня днем, когда я читал, я увидел много кода Windows на этом веб-сайте и другие, которые передают данные, которые не являются изменчивыми для потока, и, похоже, нет никакого барьера памяти или чего-то еще. Я знаю, что C ++ или, по крайней мере, более старые версии не «поддерживают потоки», поэтому мне интересно, может быть, есть какая-то другая причина. Я думаю, что компилятор видит, что я передал указатель &яблоко в вызове CreateThread (), поэтому он знает, чтобы выписать членов apple до звонка.

Спасибо

5

Решение

Нет. Все функции потока Win32 позаботятся о необходимых барьерах памяти. Все записи до CreateThread видны новой теме. Очевидно, что чтения в этом недавно созданном потоке не могут быть переупорядочены до вызова CreateThread,

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

5

Другие решения

Нет не должно быть volatile, В то же время вы указываете на действительную проблему. Подробная работа с кешем описана в статьях Intel / ARM / etc.

Тем не менее вы можете смело предположить, что данные БУДЕТ ПИСАТЬ. В противном случае слишком много вещей будет сломано. Несколько десятилетий опыта говорят, что это так.

Если планировщик потока запустит поток на том же ядре, состояние кеша будет в порядке, иначе, если нет, ядро ​​очистит кеш. Иначе ничего не получится.

Никогда не используйте volatile для взаимодействия между потоками. Это инструкция о том, как обрабатывать данные только внутри потока (использовать копию регистра или всегда перечитывать и т. Д.).

2

Во-первых, я думаю, что оптимизатор не может изменить порядок за счет правильности. CreateThread () — это функция, параметр binidng для вызова функции происходит до звонок сделан.

Во-вторых, летучий не очень полезно для цели, которую вы намереваетесь. Проверьте эту статью.

1

Вы боретесь с проблемой, и создаете по крайней мере два других …

  1. Не беспокойтесь о параметре, заданном для CreateThread: если они существуют во время создания потока, они существуют до тех пор, пока CreateThread не вернется. И поскольку поток, который их создает, не уничтожает их, они также доступны для другого потока.
  2. Теперь возникает проблема: кто и когда их уничтожит: вы создаете их с new поэтому они будут существовать до delete вызывается (или пока процесс не прекратится: хорошая утечка памяти!)
  3. Процесс завершается, когда завершается его основной поток (и все другие потоки также будут завершаться операционной системой!). И в вашем главном нет ничего, что заставляет его ждать завершения другого потока.
  4. Осторожно при использовании низкоуровневых API, таких как CreateThread языки форм, которые имеют собственную библиотеку, также связаны с потоком. C-Runtime имеет _beginthreadex, Это вызов CreateThread и выполнить также другую задачу инициализации библиотеки C ++, которую вы иначе пропустите. Некоторые функции библиотеки C (и C ++) могут работать неправильно без этих инициализаций, которые также необходимы для правильного освобождения ресурсов времени выполнения при завершении. Unsing CreateThread это как использовать malloc в контексте, где delete затем используется для очистки.

Правильное поведение основного потока должно быть

// create the data
// create the other thread
// // perform othe task
// wait for the oter thread to terminate
// destroy the data

В документации Win32 API не ясно сказано, что каждый HANDLE является waitable, и стать сигнальным, когда связанный ресурс освобожден.
Чтобы дождаться завершения другого потока, вам нужно вызвать основной поток

WaitForSingleObject(hthread,INFINITE);

Так что основной поток будет правильнее:

{
data* pdata = new data;
HANDLE hthread = (HANDLE)_beginthreadex(0,0,yourprocedure, pdata,0,0);
WaitForSingleObject(htread,INFINITE);
delete pdata;
}

или даже

{
data d;
HANDLE hthread = (HANDLE)_beginthreadex(0,0,yourprocedure, &d,0,0);
WaitForSingleObject(htread,INFINITE);
}
0

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

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

Редактировать:
Я думаю, что примеры на вики-странице — хорошее объяснение http://en.wikipedia.org/wiki/Volatile_variable

0
По вопросам рекламы [email protected]