В 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
до звонка.
Спасибо
Нет. Все функции потока Win32 позаботятся о необходимых барьерах памяти. Все записи до CreateThread
видны новой теме. Очевидно, что чтения в этом недавно созданном потоке не могут быть переупорядочены до вызова CreateThread
,
volatile
не добавит никаких дополнительных полезных ограничений на компилятор и просто замедлит код. На практике это не будет заметно по сравнению со стоимостью создания новой темы.
Нет не должно быть volatile
, В то же время вы указываете на действительную проблему. Подробная работа с кешем описана в статьях Intel / ARM / etc.
Тем не менее вы можете смело предположить, что данные БУДЕТ ПИСАТЬ. В противном случае слишком много вещей будет сломано. Несколько десятилетий опыта говорят, что это так.
Если планировщик потока запустит поток на том же ядре, состояние кеша будет в порядке, иначе, если нет, ядро очистит кеш. Иначе ничего не получится.
Никогда не используйте volatile
для взаимодействия между потоками. Это инструкция о том, как обрабатывать данные только внутри потока (использовать копию регистра или всегда перечитывать и т. Д.).
Во-первых, я думаю, что оптимизатор не может изменить порядок за счет правильности. CreateThread () — это функция, параметр binidng для вызова функции происходит до звонок сделан.
Во-вторых, летучий не очень полезно для цели, которую вы намереваетесь. Проверьте эту статью.
Вы боретесь с проблемой, и создаете по крайней мере два других …
new
поэтому они будут существовать до delete
вызывается (или пока процесс не прекратится: хорошая утечка памяти!)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);
}
Я думаю, что вопрос действителен в другом контексте.
Как уже указывалось другими, использование структуры и содержимого является безопасным (хотя доступ к данным должен быть синхронизирован).
Однако я думаю, что вопрос верен, если у вас есть атомарная переменная (или указатель на нее), которая может быть изменена вне потока. Мое мнение в этом случае будет то, что в этом случае следует использовать энергозависимые.
Редактировать:
Я думаю, что примеры на вики-странице — хорошее объяснение http://en.wikipedia.org/wiki/Volatile_variable