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

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

関連記事

C++の値渡しと参照渡しの使い分けや違いについて

<目次> (1) C++の値渡しと参照渡しの使い分けや違いについて  (1-1) 「値渡し」とは?  (1-2) 「参照渡し」とは?  (1-3) 「値渡し」と「参照渡し」の使い分け (1) C++の …

no image

C++のマルチスレッド処理のサンプルや概要について

  <目次> (1) C++のマルチスレッド処理のサンプルや概要について  (1-1) C++のマルチスレッドの概要  (1-2) 構文1:関数ポインタ式  (1-3) 構文1(例):関数ポ …

C++の「->」(アロー演算子)とは?意味や使い方をご紹介

  <目次> (1) C++の「->」(アロー演算子)とは?意味や使い方をご紹介  (1-1) C++の「->」(アロー演算子=arrow operator)とは?  (1-2) …

C++の「::」の記載の意味について

  <目次> (1) C++の「::」の記載の意味について  (1-1) 「::」の意味は?  (1-2) サンプル①:スコープ演算子「::」を使ってグローバル変数にアクセス  (1-3) …

Valgrindとは?使い方や概要をご紹介(メモリリーク検知ツール)

  <目次> (1) Valgrindとは?使い方や概要をご紹介(メモリリーク検知ツール)  (1-1) Valgrindとは?  (1-2) Valgrindの導入方法  (1-3) Va …

  • English (United States)
  • 日本語
Top