Самое простое (и самое радикальное) решение— указать ключ /NUMPROC=1 (или /ONECPU) в файле boot.ini, одним росчерком пера превратив многопроцессорную систему в однопроцессорную. Правда, о производительности после этого можно забыть, поэтому прибегать к такому "варварскому" методу стоит только в самых крайних случаях, когда система регулярно сбоит, а времени на поиски неисправности и капитальный ремонт у нас нет.
Кстати, поиск неисправностей – самое сложное дело. Некорректная синхронизация потоков приводит к порче данных и критические ошибки возникают (если они вообще возникают, хуже когда программа делает из обрабатываемых данных "винегрет") довольно далеко от места "аварии". То же самое относится и к голубым экранам смерти. Изучение дампов памяти дает довольно скудную информацию, особенно если разрушены структуры данных, хранящиеся в динамической памяти, которая каждый раз выделяется по разным адресам, что затрудняет воспроизведение ошибки.
При наличии исходных текстов в первую очередь проверьте: не используется ли во многопоточной программе однопоточные версии библиотек? В частности, компилятор Microsoft Visual C++ поставляется с двумя версиями статических Си-библиотек: LIBC.LIB – для однопоточных и LIBCMT.LIB – для многопоточных программ. Динамически компонуемая библиотека MSVCRT.LIB используется как в одно- так и во многопоточных проектах. Так же поищите прямые вызовы CreateThread(). Со стандартной Си-библиотекой они _не_ совместимы и потому должны быть в обязательном порядке заменены на _beginthread() или _beginthreadex().
Все глобальные переменные (кроме тех, что используются для обмена данных между потоками) поместите в TLS (Thread Local Storage – Локальная Память Потока). На уровне исходных текстов это делается так: "__declspec (thread) int my_var;", при этом компилятор создает в PE-файле специальную секцию .tls, куда и помещает my_var, автоматически создавая отдельный экземпляр для каждого из потоков.