线程不安全函数学习
转自:https://blog.csdn.net/qq_26499321/article/details/72085592,https://blog.icrystal.top/archives/13.html
1、线程不安全
- 线程安全 就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 幂等。
- 线程不安全 就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
1.1 分类
- 不保护共享变量的函数。函数中访问全局变量和堆,需要通过同步机制来保护共享数据,比如加锁。
- 返回指向静态变量的指针的函数。某些函数(如gethostbyname)将计算结果放在静态结构中,并返回一个指向这个结构的指针。在多线程中一个线程调用的结构可能被另一个线程覆盖。可以通过重写函数和加锁拷贝技术来消除。
2、举例localtime
std::localtime这个函数功能是将time_t转换成本地时间的结构体tm。是线程不安全的。
#include <iostream> #include <time.h> using namespace std; int main() { time_t start = time(NULL); time_t end = start + 1000; struct tm* ptm_start = localtime(&start); // <-------- struct tm* ptm_end = localtime(&end); // <-------- char str_start[64] = { 0 }; char str_end[64] = { 0 }; strftime(str_start, 64, "%H:%M:%S", ptm_start); strftime(str_end, 64, "%H:%M:%S", ptm_end); printf("start: %s\n", str_start); printf(" end: %s\n", str_end); cout<<"equal:"<<(ptm_start==ptm_end)<<"\n";//两者指向相同的地址 return 0; } //输出: start: 20:49:47 end: 20:49:47 equal:1
输出结果却一样。localtime函数签名如下:
struct tm* localtime(const time_t* timep);
函数返回的是一个指针,但是外部却不需要释放这个指针指向的内存,因此其内部使用了一个全局变量或函数内部的静态变量。因此两次调用该函数,第2次的结果就覆盖了第1次的结果(二者返回的指针指向同一个地址)。
使用线程安全的函数localtime_r替代。
#include <iostream> #include <time.h> int main() { time_t start = time(NULL); time_t end = start + 1000; struct tm tm_start; struct tm tm_end; localtime_r(&start, &tm_start); localtime_r(&end, &tm_end); char str_start[64] = { 0 }; char str_end[64] = { 0 }; strftime(str_start, 64, "%H:%M:%S", &tm_start); strftime(str_end, 64, "%H:%M:%S", &tm_end); printf("start: %s\n", str_start); printf(" end: %s\n", str_end); return 0; } //输出: start: 21:05:26 end: 21:22:06
因为最初编写CRT时还没有多线程技术,所以很多函数内部使用了全局变量或函数内部的静态变量。随着多线程技术的出现,很多函数都有了对应的多线程安全版本。