프로그래밍/리눅스 프로그래밍

[Multi thread] Multi thread 에서 localtime 문제

jinkwon.kim 2020. 1. 31. 11:13
728x90
반응형

1. 목표

  multi thread에서 시간을 관리하는 방법을 알아 보겠습니다. 

꼭 C++에 만 해당되는 내용은 아닙니다. C library를 사용한다면 발생 합니다.

 

2. multi thread에서의 시간 함수 사용시 주의해야 할 사항

  multi thread 환경에서 gmtime() 함수와  localtime() 함수를 같이 사용하면 시간 값에서 충돌이 발생 합니다. 왜냐하면 내부적으로 gmtime과 localtime는 같은 시간 변수를 공유 하기 때문에 문제가 발생 합니다. 이걸 방지 하기위해서 C Library에서는gtime_r() 과 localtime_r()을 제공 합니다. 이 함수들은 내부적으로 "시간 변수" 서로 유 하지 않습니다.

 

c library 내부에서 struct *tm을 공유 

  1) 증명 

    (1) 코드 

#include <iostream>                                                                    
#include <time.h>                                                                      
                                                                                       
int main()                                                                             
{                                                                                      
    struct tm tstruct; // 기본 사용가능                                                
    time_t lt = time(NULL);                                                            
                                                                                       
    std::cout << "gmtime() address      : " << gmtime(&lt) << std::endl;               
    std::cout << "localtime() addres    : " << localtime(&lt) << std::endl;            
    std::cout << std::endl;                                                            
    std::cout << "struct tm 변수addres  : " << &tstruct << std::endl;                  
    std::cout << "localtime_r() address : " << localtime_r(&lt, &tstruct) << std::endl;
    std::cout << "gmtime_r() address    : " << gmtime_r(&lt, &tstruct) << std::endl;   
                                                                                       
    return 0;
}

    (2) 결과 

[root@localhost cpp_test]# ./local_time 
gmtime() address      : 0x7fe335c23d20
localtime() addres    : 0x7fe335c23d20

struct tm 변수addres  : 0x7ffcc859dab0
localtime_r() address : 0x7ffcc859dab0
gmtime_r() address    : 0x7ffcc859dab0

 

 

참조 : http://www.cplusplus.com/reference/ctime/gmtime/ 에서 data races에 설명 되어있습니다. 

 

3. 필요 헤더

  #include <time.h>

 

4. multi thread에서 사용 가능한 시간 함수 

  1)  struct tm *localtime_r(const time_t *timep, struct tm *result);

    - localtime에 맞춰 시간 구하는 함수 

  2)  struct tm *gmtime_r(const time_t *timep, struct tm *result); 

    - UTC에 맞춰 시간을 구하는 함수

 

5. multi thread에서 증명

  1) 시나리오 

    - 2개의 Thread(thread1, thread2)를 돌립니다.

    - thread1에서는 gmtime_r()을 사용하여 localtime_r과의 충돌을 유도 합니다.

    - thread2에서는 localtime_r()을 사용하여 gmtime_r과의 충돌을 유도 합니다.

 

  2) 예제 코드

    - thread구현시 lambda를 사용 하였습니다. ([C++ 개발자되기] 4. lambda 사용법)

#include <iostreaem>
#include <sys/timeb.h>
#include <time.h>


int main()                                                                  
{                                                                           
    std::thread thread1(                                                    
            [](int count)                                                   
            {                                                               
                struct tm tstruct; // 기본 사용가능                         
                std::ostringstream oss;                                     
                char buf[128];                                              
                                                                            
                for (int i = 0; i < count; i++) {                           
                    time_t t = time(NULL);                                  
                    if (nullptr != gmtime_r(&t, &tstruct)) {                
                        strftime(buf, sizeof(buf), "%Y-%m-%d %T", &tstruct);
                        oss << buf; // 연-월-일-시-분-초                    
                    }                                                       
                }                                                           
            }, 100000);                                                     
                                                                            
    std::thread thread2(                                                    
            [](int count)                                                   
            {                                                               
                struct tm tstruct; // 기본 사용가능                         
                std::ostringstream oss;                                     
                char buf[128];                                              
                                                                            
                for (int i = 0; i < count; i++) {                           
                    time_t t = time(NULL);                                  
                    if (nullptr != localtime_r(&t, &tstruct)) {             
                        strftime(buf, sizeof(buf), "%Y-%m-%d %T", &tstruct);
                        oss << buf; // 연-월-일-시-분-초                    
                    }                                                       
                    std::cout << "B " << oss.str() << std::endl;            
                    oss.str("");                                            
                }                                                           
            }, 100000);                                                     
     thread1.join();                                                        
     thread2.join();                                                        
    return 0;                                                               
}                                                                                                                                        

  3) 결과 

    - localtime 시간만이 정확하게 나옴

[root@localhost cpp_test]# date; ./local_time | uniq 
2020. 02. 02. (일) 15:28:21 KST
B 2020-02-02 15:28:21

 

6. 번외

  1) multi thread에서 localtime을 사용 한다고하면 어떻게 될 것인가??

    - 5번과 동일한 시나리오 사용

#include <iostream>                                                        
#include <sstream>                                                         
#include <thread>                                                          
                                            
int main()                                                                 
{                                                                          
    std::thread thread1(                                                   
            [](int count)                                                  
            {                                                              
                struct tm *tstruct; // 기본 사용가능                       
                std::ostringstream oss;                                    
                char buf[128];                                             
                                                                           
                for (int i = 0; i < count; i++) {                          
                    time_t t = time(NULL);                                 
                    if (tstruct = gmtime(&t)) {                            
                        strftime(buf, sizeof(buf), "%Y-%m-%d %T", tstruct);
                        oss << buf; // 연-월-일-시-분-초                   
                    }                                                      
                }                                                          
            }, 10000);                                                     
                                                                           
    std::thread thread2(                                                   
            [](int count)                                                  
            {                                                              
                struct tm *tstruct; // 기본 사용가능                       
                std::ostringstream oss;                                    
                char buf[128];                                             
                                                                           
                for (int i = 0; i < count; i++) {                          
                    time_t t = time(NULL);                                 
                    if (tstruct = localtime(&t)) {                         
                        strftime(buf, sizeof(buf), "%Y-%m-%d %T", tstruct);
                        oss << buf; // 연-월-일-시-분-초                   
                    }                                                      
                    std::cout << "B : : " <<oss.str() << std::endl;        
                    oss.str("");                                           
                }                                                          
            }, 10000);                                                     
     thread1.join();                                                       
     thread2.join();                                                       
    return 0;                                                              
}                                                                          

  2) 예제 코드

 

  3) 결과

    - gmtime과 localtime이 섞여서 나옴.

[root@localhost cpp_test]# date; ./local_time | uniq
2020. 02. 02. (일) 15:30:05 KST
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05
B : : 2020-02-02 06:30:05
B : : 2020-02-02 15:30:05

 

7. 결론

  - multi thread든 아니든 그냥 안전하게 localtime_r()과 gmtime_r()을 사용 합시다. 코드 한줄만 더 치면 됩니다.

 

8. 연관 포스트

>>[C++ 관련 모든 글 보기]

728x90
반응형