long i; // глобальная переменнаяvoid f() // вызывается из нескольких потоков выполнения одновременно
{
i = OSVersion(); // возврашает каждый раз одно и то же
/* дальше используется i. больше нигде она не используется */
}
Здравствуйте, Alexander G, Вы писали:
AG>Есть ли тут гонка ?
...
Может тогда лучше так?
void f() // вызывается из нескольких потоков выполнения одновременно
{
static long const i = OSVersion(); // возврашает каждый раз одно и то же
/* дальше используется i. больше нигде она не используется */
}
Мне кажется, с учетом условий в комментах, гонки нет.
Re: Race condition
От:
Аноним
Дата:
02.01.09 12:30
Оценка:
Здравствуйте, Alexander G, Вы писали:
AG>Есть ли тут гонка ?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Alexander G, Вы писали:
AG>>Есть ли тут гонка ?
А>Да, есть.
А почему? Вот одно из определений race condition.
A race condition is a flaw in a system or process whereby the output and/or result of the process is unexpectedly and critically dependent on the sequence or timing of other events.
Мой, краткий перевод: Race condition — ошибка из-за зависимости результата от последовательности событий.
Значение i везде одинаково и не меняется, значит нет. Конечно, если топикстартер под словом используется понимает и изменяется, то тогда да, есть.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, byleas, Вы писали:
B>>Здравствуйте, sc, Вы писали:
sc>>>Может тогда лучше так? B>>Это ничего не меняет, в контексте темы.
А>Ну почему же? А>Гонка становится более очевидной, А>если прикинуть, как инициируются подобные статические переменные
И как они инициируются? Хотелось бы на примере увидеть.
Здравствуйте, sc, Вы писали:
sc>Здравствуйте, Alexander G, Вы писали:
AG>>Есть ли тут гонка ? sc>... sc>Может тогда лучше так? sc>
sc>void f() // вызывается из нескольких потоков выполнения одновременно
sc>{
sc> static long const i = OSVersion(); // возврашает каждый раз одно и то же
sc> /* дальше используется i. больше нигде она не используется */
sc>}
sc>
sc>Мне кажется, с учетом условий в комментах, гонки нет.
Вот теперь точно есть.
static long i = OSVersion(); // возврашает каждый раз одно и то же
0041142D mov eax,dword ptr [$S1 (41817Ch)]
00411432 and eax,1
00411435 jne f+6Ch (41145Ch)
00411437 mov eax,dword ptr [$S1 (41817Ch)]
0041143C or eax,1
0041143F mov dword ptr [$S1 (41817Ch)],eax
//тыдымс, переменная "мы проинициализировались" уже true, но сама переменная i еще не проинициализирована
//если сейчас управление получит другой поток - будет ой
00411444 mov dword ptr [ebp-4],0
0041144B call OSVersion (411104h)
Здравствуйте, Sergey Chadov, Вы писали:
SC>Вот теперь точно есть. SC>
SC> static long i = OSVersion(); // возврашает каждый раз одно и то же
SC>0041142D mov eax,dword ptr [$S1 (41817Ch)]
SC>00411432 and eax,1
SC>00411435 jne f+6Ch (41145Ch)
SC>00411437 mov eax,dword ptr [$S1 (41817Ch)]
SC>0041143C or eax,1
SC>0041143F mov dword ptr [$S1 (41817Ch)],eax
SC>//тыдымс, переменная "мы проинициализировались" уже true, но сама переменная i еще не проинициализирована
SC>//если сейчас управление получит другой поток - будет ой
SC>00411444 mov dword ptr [ebp-4],0
SC>0041144B call OSVersion (411104h)
SC>
Спасибо, не знал, что со статиком такая засада может быть. Век живи — век учись
Здравствуйте, Alexander G, Вы писали:
AG>Можете обосновать, показать ?
Зависит от платформы. Какая тебя интересует?
А вообще, если хочешь жить спокойно, то тупо сделай одну функцию,
которая гарантированно инициализирует все твои глобальные переменные
(которых много быть не должно), до того, как запустится 2-й поток.
Ты же надеюсь не создаешь потоки при инициализации глобальных данных.
Один из положительных эффектов такого упражнения — ты будешь лучше контролировать глобальные данные,
которые всегда требуют особого внимания.
Re[5]: Race condition
От:
Аноним
Дата:
03.01.09 20:19
Оценка:
Здравствуйте, sc, Вы писали:
sc>И как они инициируются? Хотелось бы на примере увидеть.
Тебе уже ответили.
Статические переменные по типу, как ты предложил, в С++ в принципе создают кучу неприятностей
в многопоточном приложении.
Глобальные данные в этом смысле даже безопаснее, поскольку их можно
попытаться инициировать до запуска дополнительных потоков.
Здравствуйте, Аноним, Вы писали:
А>Зависит от платформы. Какая тебя интересует?
MSVC8 (2005) Windows XP Vista x86 x64
А>А вообще, если хочешь жить спокойно, то тупо сделай одну функцию, А>которая гарантированно инициализирует все твои глобальные переменные А>(которых много быть не должно), до того, как запустится 2-й поток. А>Ты же надеюсь не создаешь потоки при инициализации глобальных данных. А>Один из положительных эффектов такого упражнения — ты будешь лучше контролировать глобальные данные, А>которые всегда требуют особого внимания.
Так и сделаю. Просто, если бы не гонки, обычная глобальная переменная удобнее в моём случае.
Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ?
Русский военный корабль идёт ко дну!
Re[5]: Race condition
От:
Аноним
Дата:
03.01.09 20:57
Оценка:
Здравствуйте, Alexander G, Вы писали:
AG>Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ?
Ну например ошибка в том, что ты думаешь, что операция "первый поток изменил переменную с 0 до 6" — атомарна.
Кстати, меняешь ты не с 0 а со случайной величины.
Здравствуйте, Аноним, Вы писали:
AG>>Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ?
А>Ну например ошибка в том, что ты думаешь, что операция "первый поток изменил переменную с 0 до 6" — атомарна.
И что из не-атомарности следует ?
А>Кстати, меняешь ты не с 0 а со случайной величины.
Точно ? Вроде как:
3.8/6
The memory occupied by any object of static storage duration shall be
zero-initialized at program startup before any other initialization
takes place. [Note: in some cases, additional initialization is done
later. ]
Здравствуйте, Alexander G, Вы писали:
AG>Есть ли тут гонка ?
Если sizeof(long)>sizeof(int), то, скорее всего, запись будет неатомарна — в том смысле, что другой поток, не позаботившийся о вызове f(), может прочесть комбинацию из половинки OSVersion и нуля.
Если же каждый поток обязательно вызывает f(), то нужно иметь особо извращённую платформу, на которой перезапись того же самого значения проходит через промежуточную порчу переменной.
Но кстати, раз f() вызывается в каждом потоке — так, может быть, стоит использовать TLS?
Для MS-specific есть __declspec(thread), для других компиляторов надо читать документацию. Или вызывать соответствующие функции ОС.
Здравствуйте, Кодт, Вы писали:
К>Если sizeof(long)>sizeof(int), то, скорее всего, запись будет неатомарна — в том смысле, что другой поток, не позаботившийся о вызове f(), может прочесть комбинацию из половинки OSVersion и нуля. К>Если же каждый поток обязательно вызывает f(), то нужно иметь особо извращённую платформу, на которой перезапись того же самого значения проходит через промежуточную порчу переменной.
Вот. Хотелось бы подробнее про эту платформу
Также интересно, как в ней повлияет замена присвоения на memcpy. И замена long на long volatile. И замена long на bool.
К>Но кстати, раз f() вызывается в каждом потоке — так, может быть, стоит использовать TLS?
На самом деле задача ставится так. Есть глобальная структура, инициализировванная статичeски:
struct data_t
{
void * p1;
void * p2;
int i3;
...
bool initialized;
} data = {0};
Есть функция для инициализации, которая может быть в редких случаях вызвана из нескольки потоков одновременно:
Здравствуйте, Alexander G, Вы писали:
AG>Т.е никакая не init-once, скорее init-Nce AG>Задача в том можно ли это сделать работоспособным без блокировок.
Если ты мамой клянёшься, что пишешь туда всегда одно и то же, то гонки здесь не возникают.
Разве что для надёжности можешь заменить неатомарные присваивания атомарными — InterlockedExchange / InterlockedCompareExchange.
Ещё можно подумать о проверках, скажем, таких
void perfect_assign(long& var, long value)
{
long old = InterlockedCompareExchange(&var, value, 0); // перезаписываем, только если там 0
assert(old == 0 || old == value);
}
long perfect_read(long& var) // для проверки initialized
{
return InterlockedCompareExchange(&var, 0, 0); // единственная засада - это создаётся барьер памяти
}
А заморачиваться в сторону универсальных lock-free алгоритмов... Ну, можно, конечно, но в твоей постановке задачи не нужно.