Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

C++

C++のlocaltime関数がスレッドアンセーフである理由と使用上の注意点

投稿日:2021年10月16日 更新日:

 

<目次>

(1) C++のlocaltime関数がスレッドアンセーフである理由と使用上の注意点
 (1-1) localtime関数がスレッドアンセーフである理由
 (1-2) localtime関数がスレッドアンセーフによるNG例
 (1-3) localtime_r関数を使ったスレッドセーフなOK例
 (1-4) 他のスレッドアンセーフな関数

(1) C++のlocaltime関数がスレッドアンセーフである理由と使用上の注意点

(1-1) localtime関数がスレッドアンセーフである理由

localtime関数が時刻を取得し、その結果を詰める領域「struct tm*」が「staticな領域(全インスタンス間で共有される)」であり、スレッド多重で別スレッドと処理がぶつかった際は、お互いの結果を参照/更新できてしまう事から、スレッドアンセーフとされています。
 
(構文)
struct tm * localtime (const time_t * XXXX);

目次にもどる

(1-2) localtime関数がスレッドアンセーフによるNG例

まずはNG例のご紹介で、localtime関数を使ったスレッドアンセーフな例をを見てみます。シンプルに表現するため、シングルスレッドでシリアルに(直列に)処理を実行して、localtime関数を複数回実行すると、結果が上書きされてしまう様子を見てみます。
 
(NG例)スレッドアンセーフな例
#include <time.h>
#include <iostream>
#include <chrono>
#include <thread>
#pragma warning(disable: 4996)
using namespace std;

int main ()
{
    time_t t1 = time(nullptr);
    this_thread::sleep_for(chrono::seconds(2));
    time_t t2 = time(nullptr);
    
    //# tm*はstaticな領域を参照しており、
    //# この後、新規にlocaltime関数をコールした場合に上書きされてしまう。
    
    //# (1)localtimeの初回呼び出し(t1のlocaltime結果「t1_tm」を出力)
    tm* t1_tm = localtime(&t1);
    cout<<"t1 (Before): "<<asctime(t1_tm)<<'\n';
    
    //# (2)localtimeの2回目呼び出し(t1_tmはt2_tmの値で上書きされてしまう事の確認)
    tm* t2_tm = localtime(&t2);
    cout<<"t1 (After): "<<asctime(t1_tm)<<'\n';
    cout<<"t2        : "<<asctime(t2_tm)<<'\n'; 
    
    return 0;
}

(図111①)

・実行結果
(図111②)

目次にもどる

(1-3) localtime_r関数を使ったスレッドセーフなOK例

次にOK例として、localtime_r関数を使ったスレッドセーフな例を見てみます。localtime_r関数の構文は次のようになっており、結果を詰める領域(struct tm *YYYY)を自身で指定できるため、スレッド多重の状況下においてもお互いの結果に影響しないよう、自身でコントロールが可能です。

(構文)
struct tm *localtime_r( const time_t *XXXX, struct tm *YYYY );

(OK例)

#include <time.h>
#include <iostream>
#include <chrono>
#include <thread>
#pragma warning(disable: 4996)
using namespace std;

int main ()
{
    time_t t1 = time(nullptr);
    tm t1_buf;
    this_thread::sleep_for(chrono::seconds(2));
    time_t t2 = time(nullptr);
    tm t2_buf;
    
    //# tm*はstaticな領域を参照しており、
    //# この後、新規にlocaltime関数をコールした場合に上書きされてしまう。
    
    //# (1)localtimeの初回呼び出し(t1のlocaltime結果「t1_tm」を出力)
    //tm* t1_tm = localtime(&t1);
    localtime_r(&t1,&t1_buf);
    cout<<"t1 (Before): "<<asctime(&t1_buf)<<'\n';
    
    //# (2)localtimeの2回目呼び出し(t1_tmはt2_tmの値で上書きされてしまう事の確認)
    //#
    //tm* t2_tm = localtime(&t2);
    localtime_r(&t2,&t2_buf);
    cout<<"t1 (After): "<<asctime(&t1_buf)<<'\n';
    cout<<"t2        : "<<asctime(&t2_buf)<<'\n'; 
    
    return 0;
}

(図131)

・実行結果
(図132)

目次にもどる

Adsense審査用広告コード


Adsense審査用広告コード


-C++

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

ポインタと参照の違いについてサンプルPGを使ってご紹介

  <目次> (1) ポインタと参照の違いについてサンプルPGを使ってご紹介  (1-1) ポインタと参照の概要  (1-2) 両者の違い①:宣言/初期化  (1-3) 両者の違い②:再代入 …

C++をLinuxでコンパイルする方法について

  <目次> (1) C++をLinuxでコンパイルする方法について  (1-1) STEP0:【事前準備】ターミナル機能の準備(ターミナル、Teraterm、Putty)  (1-2) S …

C++のfork関数の構文や使い方について

  <目次> (1) C++のfork関数の構文や使い方について  (1-1) fork()関数とは?概要や目的  (1-2) fork()関数の構文  (1-3) fork()関数のサンプ …

C++の共用体とは?構造体との違いや使用方法について

  <目次> (1) C++の共用体とは?構造体との違いや使用方法について  (1-1) 共用体とは?構造体との違いは?  (1-2) 共用体の定義方法、変数定義方法、初期化方法  (1-3 …

C++で変数にアスタリスク(*)が2つ付いている意味について

  <目次> (1) C++で変数にアスタリスク(*)が2つ付いている意味について  (1-1) 変数にアスタリスク(*)が2つ付いている意味  (1-2) 構文  (1-3) サンプルプロ …

  • English (United States)
  • 日本語
Top