【Linux C | 时间】localtime 的介绍、死机、死锁问题以及 localtime_r 函数的时区问题

发布于:2025-02-27 ⋅ 阅读:(14) ⋅ 点赞:(0)

😁博客主页😁:🚀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 函数的线程不安全问题,以及解决方法,并提供代码例子加深理解。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考:
localtime线程不安全与localtime_r时区问题