【问题标题】:<time.h> Shared Internal Object - struct tm<time.h> 共享内部对象 - struct tm
【发布时间】:2016-09-09 13:11:20
【问题描述】:

我正在研究 C 时间库的例程,因为我需要一种方法来跟踪程序日志文件中的时间。我发现做到这一点的方法是拥有一个time_t 对象,它仅包含自 1970 年 1 月 1 日 00:00 UTC 以来的秒数。然后,我将这个time_t 对象解析为localtime(time_t* argument) 例程,这将返回一个指向tm 结构的指针。后者将把时间保存在一个更复杂的结构中,从 1970 年 1 月 1 日起,以秒为单位转换年、月、日、小时等的时间。tm 结构指针最终可以被asctime(strcut tm* argument) 用来返回日志的人类友好时间(即 2016 年 9 月 7 日星期三 13:45:23)。

我的问题是关于 struct tm 对象。正如我们在Cplusplus 的示例代码中看到的那样,从未声明过struct tm 对象,只有一个指针。这意味着该对象在其他地方声明,我们只是在访问它。参考链接本身指出:

"The function also accesses and modifies a shared internal object,
which may introduce data races on concurrent calls to gmtime and
localtime. Some libraries provide an alternative function that avoids
this data race: localtime_r (non-portable)."

那么,谁创建了struct tm 对象?是第一次加载的C时间库吗?这意味着加载库的第一个进程将声明该对象,而所有其他进程将只与已声明的对象共享该库?为了避免那些数据竞争问题,为每个调用创建一个新的struct tm 对象并返回一个指向它的指针,这样每个程序都有自己的结构不是更好吗?

也许每个使用 Ctime 的程序都有一个 struct tm MyProgramStruct; localtime(&amp;Rawtime, &amp;MyProgramStruct); 而不是一个结构?有什么理由这样做吗?

最后,使用 C 时间库 localtime 例程是不是一种不好的做法,因为程序不同步可能导致错误输出?

链接中的示例代码:

/* localtime example */
#include <stdio.h>      /* puts, printf */
#include <time.h>       /* time_t, struct tm, time, localtime */

int main ()
{
  time_t rawtime;
  struct tm * timeinfo;

  time (&rawtime);
  timeinfo = localtime (&rawtime);
  printf ("Current local time and date: %s", asctime(timeinfo));

  return 0;
}

【问题讨论】:

  • 当然,那是个坏主意。不是唯一的,考虑 errno、strtok、setlocale、gmtime、fcvt。每个 CRT 实现都需要解决这个令人头疼的问题,它们的做法都不同。要是那时他们有时光机就好了……

标签: c time shared-libraries


【解决方案1】:

那么,谁创建了 struct tm 对象?是第一次加载的C时间库吗?

根据localtime() 函数的文档,它返回一个指向静态分配 结构的指针。该结构属于 C 库;该函数提供了一个指向它的指针。它的确切位置以及存储分配的确切时间和方式的详细信息并不重要,并且可能因实施而异。您只需要了解同一进程的不同调用使用并提供指向同一结构的指针。

这意味着加载该库的第一个进程将声明该对象,而所有其他进程将只与已声明的对象共享该库?

没有。您无需担心结构在进程之间共享,只需担心它在多个调用之间以及在同一进程中的多个线程之间共享。

为了避免那些数据竞争问题,为每个调用创建一个新的 struct tm 对象并返回一个指向它的指针,这样每个程序都有自己的结构,这不是更好吗?也许每个使用 Ctime 的程序都有一个 struct tm MyProgramStruct; localtime(&amp;Rawtime, &amp;MyProgramStruct); 而不是一个结构?有什么理由这样做吗?

同样,这不是跨进程问题,只是进程内问题,这仍然足够糟糕。是的,这个问题可以通过不共享结构来解决,这就是函数localtime_r() 的区别所在,它是可用的。但是现有的函数不能改变来做同样的事情,因为这会给用户带来一个新的要求来释放提供的结构。

localtime() 的设计者想让它易于使用,而且确实如此,只要您不与共享数据问题发生冲突。如果你的程序是单线程的,那么你可以很容易地避免它。 localtime() 不是唯一存在此类问题的标准库函数。

最后,使用 C 时间库 localtime 例程是不是一种不好的做法,因为程序不同步可能导致错误输出?

不是因为这个原因,不是,因为不存在跨进程问题。 确实在使用localtime()和其他类似静态存储的功能时需要注意,例如strtok()。但是您可以逐个程序判断是否存在任何数据竞争——您无需担心来自未指定的其他程序的干扰。

【讨论】:

  • 感谢您把事情说清楚。我认为共享内部对象是在进程而不是线程之间共享的。正如你所说,只有在我们使用多线程的情况下才会出现问题。
猜你喜欢
  • 2016-05-25
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 2017-09-26
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多