Позволю себе цитнуть Рихтера, потому как лучше него не объяснить мне
Наверное, Вам уже хочется спросить: "А зачем мне отдельные библиотеки для однопоточных и многопоточных программ?" Отвечаю. Стандартная библиотека С была разработана где-то в 1970 году — задолго до появления самого понятия многопоточности. Авторы этой библиотеки, само собой, не задумывались о проблемах, связанных с многопоточными приложениями.
Возьмем, к примеру, глобальную переменную errno из стандартной библиотеки С. Некоторые функции, если происходит какая-нибудь ошибка, записывают в эту переменную соответствующий код. Допустим, у Вас есть такой фрагмент кода:
BOOL fFailure = (system("NOTEPAD.EXE README.TXT") == -1);
if (fFailure)
{
switch (errno)
{
case E2BIG:
// список аргументов или размер окружения слишком велик
break;
case ENOENT:
// командный интерпретатор не найден
break;
case ENOEXEC;
// неверный формат командного интерпретатора
break;
case ENOMEM:
// недостаточно памяти для выполнения команды
break;
}
Теперь представим, что поток, выполняющий показанный выше код, прерван после вызова функции system и до оператора if. Допустим также, поток прерван для выполнения другого потока (в том же процессе), который обращается к одной из функций библиотеки С, и та тоже заиосит какое то значение в глобальную переменную errno. Смотрите, что получается когда процессор вернется к выполнению первого потока, в переменной errno окажется вовсе не то значение, которое было записано функцией system. Поэтому для решения этой проблемы нужно закрепить за каждым потоком свою переменную errno. Кроме того, понадобится какой-то механизм, который позволит каждому потоку ссылаться на свою переменную errno и не трогать чужую.
Это лишь один пример того, что стандартная библиотека С/С++ не рассчитана на многопоточные приложения. Кроме errno, в ней есть еще целый ряд переменных и функций, с которыми возможны проблемы в многопоточной среде _doserrno, strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile, a<tcttme, _wascttme, gmttme, _ecvt, _Jcvt - список можно продолжить.
Чтобы многопоточные программы, использующие библиотеку С/С++, работали корректно, требуется создать специальную структуру данных и связать ее с каждым потоком, из которого вызываются библиотечные функции. Более того, они должны знать, что, когда Вы к ним обращаетесь, нужно просматривать этот блок данных в вызывающем потоке чтобы не повредить данные в каком-нибудь другом потоке.
Так откуда же система знает, что при создании нового потока надо создать и этот блок данных3. Ответ очень прост - не знает и знать не хочет. Вся ответственность — исключительно на Вас. Если Вы пользуетесь небезопасными в многопоточной среде функциями, то должны создавать потоки библиотечной функцией _begmthreadex, а не Windows-функцией CreateThread.