😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍localtime、localtime_r 函数 🍭
⏰发布时间⏰: 2025-02-26 12:38:58
本文未经允许,不得转发!!!
目录
🎄一、概述
之前有一篇文章介绍Linux下时间编程的函数的:时间获取、设置、转换 | time、gettimeofday、gmtime、localtime、mktime、ctime、asctime、strftime 。感兴趣的可以看看。
这篇文章主要介绍localtime函数的线程不安全问题:
The four functions asctime(), ctime(), gmtime() and localtime() return a pointer to static data and hence are not thread-safe.
✨1.1 localtime 函数介绍
函数原型:
#include <time.h>
struct tm *localtime(const time_t *timep);
localtime 函数的功能是将time_t *
表示的秒数,转换为struct tm *
类型的时间。localtime 会将日历时间转换成本地时间(考虑到本地时区和夏时制标志)
返回值:成功返回指向struct tm的指针。返回值指向一个静态分配的结构,该结构可能会被任何日期和时间函数的后续调用所覆盖。
✨1.2 localtime 的线程不安全问题
在多线程环境中,直接使用标准库中的 localtime 函数可能导致程序崩溃或死锁。这是因为 localtime 不是线程安全的函数,在多个线程并发调用时可能会引发竞争条件。
具体而言,localtime 内部维护了一个静态结构体变量来存储转换后的本地时间信息。当两个或更多线程几乎同时调用此函数时,它们会尝试修改同一个内存位置的数据,从而造成数据损坏甚至程序异常终止。
🎄二、localtime 返回值都指向同一块内存
localtime 调用成功后会返回指向struct tm的指针。返回值指向一个静态分配的结构,每次调用都返回这块内存的地址,只是每次调用 localtime 都会更改这块内存的值。下面通过一个例子来验证这一点:
#include <stdio.h>
#include <time.h>
int main ()
{
time_t now = time((time_t*)NULL);
time_t tomorrow = now + 24*60*60;
struct tm *info1;
struct tm *info2;
info1 = localtime( &now );
info2 = localtime( &tomorrow );
printf("info1=%p, 当前的本地时间和日期:%s", info1, asctime(info1));
printf("info2=%p, 当前的本地时间和日期:%s", info2, asctime(info2));
return(0);
}
运行结果如下:
可以看到,打印的地址是一样的,时间也是一样的。
🎄三、localtime 的死锁问题
先说结论,localtime 的实现机制中使用了pthread_mutex
,多个线程使用时可能导致死锁。
为了验证这个观点,有博主查看glibc的代码,发现localtime()也是使用了pthread_mutex来解决线程间同步的问题。如下:
time/localtime.c文件中,localtime() 调用了__tz_convert()。
time/tzset.c文件中,__tz_convert()函数中,__libc_lock_lock()函数对tzset_lock变量执行了lock操作。
sysdeps/pthread/bits/libc-lock.h文件中,定义了一个宏:
#define __libc_lock_lock(NAME) \
(__libc_maybe_call2 (pthread_mutex_lock, (&(NAME)), 0));
原文地址:Localtime导致进程死锁问题探源
所以,我们在使用 localtime 时需要特别注意加锁的问题,避免造成死锁!!!
🎄四、使用 localtime_r 函数
前面介绍了 localtime 函数的线程不安全问题,那怎么解决呢?
Linux系统中提供了另一个函数 localtime_r 来解决 localtime 的线程不安全问题。
localtime_r 函数的功能与 localtime 类似,但 localtime_r 会要求使用者传入一个 struct tm *
结构体用来存储结果。具体看下面例子:
#include <stdio.h>
#include <time.h>
int main ()
{
time_t now = time((time_t*)NULL);
time_t tomorrow = now + 24*60*60;
struct tm *info1, ret1;
struct tm *info2, ret2;
info1 = localtime_r( &now, &ret1 );
info2 = localtime_r( &tomorrow, &ret2 );
printf("info1=%p, 当前的本地时间和日期:%s", info1, asctime(info1));
printf("info2=%p, 当前的本地时间和日期:%s", info2, asctime(info2));
return(0);
}
运行结果:
有看到其他博主说,localtime_r 会有时区问题,在调用 localtime_r 之前需要先调用 tzset
函数来调整时区。依据下面这段话:
According to POSIX.1-2004, localtime() is required to behave as though tzset(3) was called, while localtime_r() does not have this requirement. For portable code tzset(3) should be called before localtime_r().
翻译:localtime像调用过了tzset一样。localtime_r没有,所以之前需要主动调用tzset,否则时区更改后localtime_r并不会生效
🎄五、总结
本文介绍了 localtime 函数的线程不安全问题,以及解决方法,并提供代码例子加深理解。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁