
localtime和mktime是用于在时间分量和时间秒数之间转换的标准c函数.
在glibc文档描述中,localtime的实现使用内部静态缓存来保存结果,因此这是一个API,不适用于多线程环境. glibc提供了线程安全的localtime_r版本. mktime没有这个问题.
因此,根据glibc文档,在多线程环境中使用localtime_r和mktime是安全的localtime 线程安全,
mktime和localtime_r的实现都考虑了时区的转换,并且时区的计算应使用全局变量tzname / timezone / daylight. 这本质上是线程不安全的.
请参阅glibc-2.3.2的源代码(下面的源代码位置均相对于源目录)
--------- time / localtime.c和time / tzset.c
在localtime_r中,调用tzset_internal来设置时区. 输入参数始终为0,因此从理论上讲,只要第一次结束,就无需初始化. 请参阅下面的代码.
但是,由于在多线程环境中引入了静态变量is_initialized,因此该实现代码存在问题. 无法保证并发执行环境的正确性.
----(时间/ tzset.c)-------
/ *解释TZ变量. * /
静态无效
内部功能
tzset_internal(始终)
int始终;

{
static int is_initialized;
注册const char * tz;
寄存器size_t l;
char * tzbuf;
unsigned short int hh,mmlocaltime 线程安全,ss;
无符号短整数规则;
如果(始终被is_initialized &&!)
返回;
is_initialized = 1;
...........
}
但是mktime不是这样的
----(时间/ mktime.c)-------

/ *将* TP转换为time_t值. * /
time_t
mktime(tp)
struct tm * tp;
{
#ifdef _LIBC
/ * POSIX.1 8.1.1要求,每当调用mktime()时,
外部变量`tzname'中包含的
时区名称
设置
就像调用tzset()函数一样. * /
__ tzset();
#endif
返回__mktime_internal(tp,my_mktime_localtime_r和localtime_offset);

}
由于定义了_LIBC,每次都会调用tzset,因此tzset的代码是这样的
----(时间/ tzset.c)-------
无效
__ tzset(无效)
{
__ libc_lock_lock(tzset_lock);
tzset_internal(1);
如果(!____ use_tzfile)
{
/ *设置`tzname'. * /
__ tzname [0] =(char *)tz_rules [0] .name;
__ tzname [1] =(char *)tz_rules [1] .name;
}

__ libc_lock_unlock(tzset_lock);
}
每次都会调用tzset_internal,并且每次都将重写时区信息.
应注意,前面的宏__libc_lock_lock在sysdeps / generic / bits / libc-lock.h中定义为:
#define __libc_lock_lock(名称)
是无操作操作,因此它不能用作同步线程.
因此,您可以看到上述glibc代码实现中存在两个问题:
1. tzset_internal is_initialized中使用的静态变量
(这可以通过在程序中定义一个无用的全局变量并程开始工作之前在其初始化中调用mktime来克服)
2. mktime必须每次都重写全局变量tzname / timezone / daylight
(这个问题基本上是没有办法解决的)
因此mktime和localtime_r不适合多线程应用程序.
有两种解决方案:
1. 自己实现mktime和localtime_r,但是时区的计算很麻烦. 当然,您不能使用时区信息,也不能使用固定时区,例如北京时区.
2. 使用pthread的互斥锁来锁定mktime和localtime_r,但是以这种方式使用pthread库,可移植性不够好.
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-217808-1.html
请拿出当年“保卫珍宝岛