多線程使用linux時間函數的方法

來源:互聯網
上載者:User

linux的時間函數有其特別需要注意的使用方法,在工程項目中,這點很容易忽視,本文就時間函數在多線程中的使用作一個小結。

首先看一個函數,取下一天的功能函數,該函數使用了時間函數localtime或者localtime_r來擷取系統時間。
int GetNextTime(int curtm)
{
 struct tm t;
 t.tm_year = curtm/10000 - 1900;
 t.tm_mon = ((curtm/100)%100 - 1);
 t.tm_mday = (curtm)%100;
 t.tm_hour = 1;
 t.tm_min = 0;
 t.tm_sec = 0;

 time_t nt;
 //struct tm *local;
 struct tm local;
 char buf[16];

 nt = mktime(&t) + 24 * 3600;
 //local = localtime(&nt); // ------------------(1)
 localtime_r(&nt, &local); // ------------------(2)
 sprintf(buf, "%4d%02d%02d%02d%02d%02d", local.tm_year+1900,local.tm_mon+1,local.tm_mday,0,0,0);
 
 return (int)(atol(buf)/1000000);
}

接著,將函數封裝線上程類中,以方便測試多線程使用時間函數localtime或者localtime_r。
class mythread : public Thread
{
 public:
  mythread():m_isRun(false) {}
  virtual void Run()
  {
   int begin = 19860101;
   int end = 20130104;
   int count = 0;
   int cur = begin;
   int next;
   while(cur != end)
   {
    next = GetNextTime(cur);
    count++;
    cur = next;
   }
   if(count != 9865) // 19860101-20130104理論上一共有9865天,如果多線程算出的不是,則列印出來
    cout << count << endl;
  }

  virtual void Stop(){m_isRun=true;}
  virtual ~mythread() {}
 private:
  bool m_isRun;
};

測試:
void threadtest()
{
 for(int k=0;k<20;k++)
 {
  mythread p[30];
  for(int i = 1;i<30;i++)
    p[i].Start();
  for(int i = 1;i<30;i++)
    p[i].Join();
  for(int i = 1;i<30;i++)
    p[i].Stop();
  cout << "the " << k << " test end!" << endl;
 }
}

結果可見,如果時間函數用(1)則,開多線程調用就會報出count不是正確的數量;而用(2)則正確。因此,linux時間函數中——
localtime函數只能用於單線程;而多線程中應該選擇用localtime_r。後者是安全執行緒的時間函數。

mktime,localtime_r,gettimeofday是安全執行緒的嗎?

一下摘自:http://hi.baidu.com/pkuyikai/item/aad084ca252966d797445246

之前為了診斷系統的檢索效能中的問題,分階段地對程式進行了計時,診斷出的結果是:記錄越多,那麼這三個階段所消耗的時間就越長。而反覆review三個階段的主要工作代碼,卻發現這些代碼根本不可能這麼耗時。慢慢地,順藤摸瓜地查出時間消耗都發生在擷取時間值的方法——mktime()上。於是大家一致懷疑是mktime()方法以及相關的localtime_r()方法都不是安全執行緒的。可其他項目中也用這些方法,為什麼他們就沒有發現這樣的問題呢?

在glibc的文檔描述中,glibc提供了一個安全執行緒的方法localtime_r來代替localtime。mktime不存線上程不安全的問題。所以,按照glibc的文檔,在多線程環境下可以安全的使用localtime_r和mktime,實際情況並非如此。找來mktime()和localtime_r()的原始碼,終於發現了奧妙所在。

mktime和localtime_r在實現上都考慮了時區的轉換,而時區的計算要使用全域變數tzname/timezone/daylight。這本質上就是線程不安全的。

通過對glibc中這兩個系統函數原始碼的分析可以知道,它們實現中有兩個問題:

1、tzset_internal 中使用的static變數is_initialized

2、mktime每次都要重寫全域變數tzname/timezone/daylight

所以mktime和localtime_r不適合於多線程應用。

 

解決方案有二:

1、自己實現mktime和localtime_r,但是這樣時區的計算是麻煩的,當然也可以不使用時區資訊,或者使用固定時區,比如北京時區,這樣就簡單多了。由於公司的伺服器基本不會有時區問題,因此我們也自己實現了這兩個方法。

2、用pthread的mutex來給mktime和localtime_r加鎖,但是這樣要使用pthread庫,移植性不夠好。

    gettimeofday()這個syscall用來供使用者擷取timeval格式的目前時間資訊(精確度為微秒級),以及系統的當前時區資訊(timezone)。結構類型 timeval的指標參數tv指向接受時間資訊的使用者空間緩衝區,參數tz是一個timezone結構類型的指標,指向接收時區資訊的使用者空間緩衝區。這 兩個參數均為輸出參數,傳回值0表示成功,返回負值表示出錯。
   

首先來看一下spin_lock機制。spin_lock機制和semaphore機制解決的都是兩個進程的互斥問題,都是讓一個進程退出臨界區後另一個進程才進入的方法,不過sempahore機制實行的是讓進程 暫時讓出cpu,進入等待隊列等待的策略,而spin_lock實行的卻是卻進程在原地空轉,等著另一個進程結束的策略。

    gettimeofday()代碼中的read_lock_irqsave(lock, flags)以及read_lock_irq(lock), read_lock_bh(lock) 和 write_lock_irqsave(lock, flags) , write_lock_irq(lock), write_lock_bh(lock)都是spin_lock的一個小小的變型,而Spin_lock採用的方式是讓一個進程運行,另外的進程忙等待,由於在只有一個cpu的機器(UP)上微觀上只有一個進程在運行。因此gettimeofday()也不是安全執行緒的系統調用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.