前言
c运行库、c标准库、windows API的区别和联系
http://www.cnblogs.com/renyuan/p/5031100.html
标准的C run-time library是1970年开发的,这个C run-time library影响深远,本来是C的函数库(包括上面提到的strtok等函数的实现的普遍使用的库),后来)到了 C++ 世界里,有另外一个概念:Standard C++ Library,它包括了上面所说的 C run-time library 和 STL。包含 C run-time library 的原因很明显,C++ 是 C 的超集,没有理由再重新来一个 C++ run-time library.而C与C++是操作系统的基础,从unix、linux到windows,所以大家重试C run-time library的是非常必要的。
一个进程中有很多全局变量以及函数(error、strtok、asctime等),各个线程对这些变量会产生干扰。在多线程运行期库中每个线程有自己的本地存储空间,有时也会使用全局变量和静态变量,如果多线程随意同时访问全局变量和静态变量,就将出现意向不到的错误,这个在现在的多线程编程中一般都会通过加锁等机制解决,但我们往往忽略了C run-time library里面的一些函数使用了全局变量和静态变量却没用相应机制避免冲突,对于strtok函数具体出问题原理是:
应用函数初次调用strtok时传递一个字串的地址,例如上面说的”aaa.bbb.dddd”,并将字串的地址保存在自己的静态变量中,当你将来再次调用strtok并传递NULL时(strtok的特殊用法,第二次调用时字串传NULL表示对第一次传进去的字串继续分隔,所以要先保存字串地址,这是有点怪异的实现),该函数就会引用保存好的字串地址。在多线程环境下,另一个线程也可能调用strtok,在这种环境下,另一个线程会在第一个线程不知道的情况下替换静态变量中的字串地址,这就会导致各种难以排除的错误出现。其他线程不安全函数原理类似,解决办法是只要给这些全局变量或静态变量加锁就行了,不过我们一般都没机会改这些库,所以使用时就要小心。
为什么
多线程环境小心多线程不安全函数
http://blog.sina.com.cn/s/blog_b1ffa5f10102vo95.html
从标准C运行库与多线程的矛盾说起,标准C运行库在1970年被实现了,由于当时没任何一个操作系统提供对多线程的支持。因此编写标准C运行库的程序员根本没考虑多线程程序使用标准C运行库的情况。比如标准C运行库的全局变量errno。很多运行库中的函数在出错时会将错误代号赋值给这个全局变量,这样可以方便调试。但如果有这样的一个代码片段:
if (system("notepad.exe readme.txt") == -1)
{
switch(errno)
{
...//错误处理代码
}
}
假设某个线程A在执行上面的代码,该线程在调用system()之后且尚未调用switch()语句时另外一个线程B启动了,这个线程B也调用了标准C运行库的函数,不幸的是这个函数执行出错了并将错误代号写入全局变量errno中。这样线程A一旦开始执行switch()语句时,它将访问一个被B线程改动了的errno。这种情况必须要加以避免!因为不单单是这一个变量会出问题,其它像strerror()、strtok()、tmpnam()、gmtime()、asctime()等函数也会遇到这种由多个线程访问修改导致的数据覆盖问题。
为了解决这个问题,Windows操作系统提供了这样的一种解决方案——每个线程都将拥有自己专用的一块内存区域来供标准C运行库中所有有需要的函数使用。而且这块内存区域的创建就是由C/C++运行库函数_beginthreadex()来负责的。
_beginthreadex()函数在创建新线程时会分配并初始化一个_tiddata块。这个_tiddata块自然是用来存放一些需要线程独享的数据。事实上新线程运行时会首先将_tiddata块与自己进一步关联起来。然后新线程调用标准C运行库函数如strtok()时就会先取得_tiddata块的地址再将需要保护的数据存入_tiddata块中。这样每个线程就只会访问和修改自己的数据而不会去篡改其它线程的数据了。因此,如果在代码中有使用标准C运行库中的函数时,尽量使用_beginthreadex()来代替CreateThread()。
所以,当使用CRT时(基本上所有的程序都使用CRT),请尽量使用_beginthread()/_beginthreadex()/_endthread()/_endthreadex()这组函数来创建线程。在MFC中,还有一组类似的函数是AfxBeginThread()和AfxEndThread(),根据上面的原理类推,它是MFC层面的线程包装函数,它们会维护线程与MFC相关的结构,当我们使用MFC类库时,尽量使用它提供的线程包装函数以保证程序运行正确。
摘自
http://www.jb51.net/article/41459.htm
http://blog.csdn.net/morewindows/article/details/7421759