Здравствуйте, apple-antonovka, Вы писали:
AA>Скомпилируйте пример ниже чтоб все что я писал выше не было голословным. И учтите что константы подобраны для максимально быстрого и показательного проявления эффекта. Увеличтье слипы, поменяйте расположение маллоков и вы запросто можете добится того что код будет работать час, сутки и больше, но потом зависнет.
AA>AA>#include <stdlib.h>
AA>#include <process.h>
AA>#include <windows.h>
AA>void __cdecl thread(void *p)
AA>{
AA>for(;)
AA>{
AA>void *p=malloc(0x100000);
AA>Sleep(1);
AA>free(p);
AA>}
AA>}
AA>int main(int argc, char* argv[])
AA>{
AA>HANDLE h = (HANDLE)_beginthread(thread,0,NULL);
AA>for(;;)
AA>{
AA>printf("[%08x] I'm still alive!\n",::GetTickCount());
AA>SuspendThread(h);
AA>void *p=malloc(0x100000);
AA>free(p);
AA>ResumeThread(h);
AA>Sleep(1);
AA>}
AA>return 0;
AA>}
AA>
В этом примере, если используется многопоточная CRT (а однопоточную тут и вовсе использовать некорректно), ф-я malloc(), вызываемая из ф-ции thread() наступает на критическую секцию, после чего основной поток останавливает поток thread(), и вызвавает все тот же malloc(). естественно возникает дедлок. Ровно таже фигня и со всеми остальными примерами.
А вот это:
AA>Спор перетекает в бесполезный флейм. Извините, но по должности я архитект и если увижу что какой то из программеров в моем проекте пишет одну из перечисленных в начале функий, я его настоятельно попрошу обойтись без нее.
ничто иное, как понтокидательство, к делу относящееся весьма косвенно.
Здравствуйте, apple-antonovka, Вы писали:
А>>Упс... А можно ли поподробнее чем череват SuspendThread в системных библиотеках (каких???). Я что-то в этой жизни похоже пропустил. здесь я даже упоминания про SuspendThread не увидел (зрение — плохое, английский ещё хуже, а поиск ничего не дал ).
AA>Вообще говоря в любых. Многие API функции используют синхронизацию для thread-safe работы со своими внутренними структурами. memory manager тоже естественно использует дабы можно было одновременно из нескольких потоков выделять/освобождать память банальнымм malloc/free/new/delete. Если поток будет заморожен в тот момент когда он находится в синхронизироанном коде, любой другой поток при доступе к синхронизированными данным зависнет. Т.е.:
AA>Поток A вызывает malloc. malloc уходит в HeapAlloc во втором параметре которого нету флага HEAP_NO_SERIALIZE. HeapAlloc вызовет RtlAllocateHeap. RtlAllocateHeap завладеет какимто мутексом в недрах ntdll дабы сериализовать работу с кучей между потоками. И в тот момент как поток А будет ковыряться в куче выделяя память, поток Б ему сделает SuspendThread. Поток А останется замороженным на середине выделения памяти. Все. Любая попытка другого потока выделить/освободить память приведет к его ожиданию пока поток А освободит мутекс. А он его не освободит потому что он suspended. Прога повисла. Если бы вместо SuspendThread юзался бы TerminateThread то поток А был бы убит во время работы с кучей, мутекс бы освободился, но куча бы осталась в поврежденном состоянии и прога бы свалилась.
AA>Пример с памятью — это просто самый показательный, на самом деле многие апи функции чегото лочат. Иначе они не были бы thread-safe.
Всё это верно только для "старых" lock-based OC.
Вот это "Иначе они не были бы thread-safe" вообще имхо устаревшее мнение, не факт, что верно для всех функций даже для современных ОС, в т.ч. для Win. Т.к. они тоже потихоньку допирают до lock-free. Достаточно поглядеть на функцию InitializeSListHead. И thread-safe и без критических секций.
lock-free подход лишён многих недостатков традиционного lock-based и в т.ч. всего описанного выше, а также deallock'ов, priority inversion, control serialization, zero scalability и т.д.
Правда на данном этапе, что касается полностью lock-free ОС, это пока "университетские" разработки. Т.ч. сейчас SuspendThread() делать не надо