1. 程式人生 > >[C++11 併發程式設計] 17 超時等待

[C++11 併發程式設計] 17 超時等待

之前我們看到的所有等待機制都是不會超時的,也就是說,等待某個同步事件的執行緒會一直掛起。有些情況下,我們希望設定一個最長等待時間,使得程式可以繼續與使用者進行互動,使得使用者可以取消這個操作。我們先來看看C++11提供的時鐘類clock:

clock

clock提供瞭如下四種資訊:

  • 當前時間
  • 存放從clock獲取到的時間的型別
  • 時鐘每個tick的週期
  • 每個tick的週期是否固定,固定則為“steady”時鐘

Member functions

now [static] returns a  representing the current point in time 
(public static member function)
converts a system clock time point to  
(public static member function)
converts  to a system clock time point 
(public static member function)

下面是一個簡單的例項,計算不同長度向量中元素的和並列印程式執行的時間:
#include <iostream>
#include <vector>
#include <numeric>
#include <chrono>
 
volatile int sink;
int main()
{
    for (auto size = 1ull; size < 1000000000ull; size *= 100) {
        // record start time
        auto start = std::chrono::system_clock::now();
        // do some work
        std::vector<int> v(size, 42);
        sink = std::accumulate(v.begin(), v.end(), 0u); // make sure it's a side effect
        // record end time
        auto end = std::chrono::system_clock::now();
        std::chrono::duration<double> diff = end-start;
        std::cout << "Time to fill and iterate a vector of " 
                  << size << " ints : " << diff.count() << " s\n";
    }
}
程式執行結果如下:
Time to fill and iterate a vector of 1 ints : 2.93e-06 s
Time to fill and iterate a vector of 100 ints : 2.94e-06 s
Time to fill and iterate a vector of 10000 ints : 8.9962e-05 s
Time to fill and iterate a vector of 1000000 ints : 0.00859845 s
Time to fill and iterate a vector of 100000000 ints : 0.948915 s
時鐘的tick週期可以通過std::ratio<x,y>來指定,一秒tick25下的時鐘週期為std::ratio<1,25>,每2.5秒tick一下的時鐘週期為std::ratio<5,2>。

tick週期穩定的時鐘被稱為穩定時鐘,它的is_steady靜態成員變數為true。對於非穩定時鐘,在本地時鐘發生漂移時,會自動進行調整,這就可能導致後執行的now()操作可能比先執行的now()操作得到的時間更小。在多執行緒環境下,超時操作需要使用穩定的時鐘。可以使用std::chrono::steady_clock來獲得穩定的時鐘。std::chrono::system_clock則被稱為實時時鐘,可以被轉換為time_t值,也可以通過time_t轉換為system_clock型別。而std::chrono::high_resolution_clock則提供了系統能支援的最高精度的時鐘。

duration

duration用於指定一個時間段,std::chrono::duration<>類模版的第一個引數指定週期的標示型別(比如int, long或者double),第二個引數指定週期的單位(一個單位代表多少秒)。

例如,以分鐘為單位的週期定義如下,一分鐘為60秒:

std::chrono::duration<short, std::ratio<60, 1>>
以毫秒為單位的週期定義如下,一秒鐘為1000毫秒:
std::chrono::duration<double, std::ratio<1, 1000>>
此外,標準庫在std::chrono名字空間還提供了一系列預定義的週期精度,例如:nanoseconds,microseconds,milliseconds,seconds,minutes和hours。這樣如果要用精度為分秒的週期,可以使用如下程式碼:
std::duration<double, std::centi>;
下面一個例子中,定義了多種型別的duration,並在它們之間進行轉換:
#include <iostream>
#include <chrono>
 
int main()
{
    using shakes = std::chrono::duration<int, std::ratio<1, 100000000>>;
    using jiffies = std::chrono::duration<int, std::centi>;
    using microfortnights = std::chrono::duration<float, std::ratio<12096,10000>>;
    using nanocenturies = std::chrono::duration<float, std::ratio<3155,1000>>;
 
    std::chrono::seconds sec(1);
 
    std::cout << "1 second is:\n";
 
    std::cout << std::chrono::duration_cast<shakes>(sec).count()
              << " shakes\n";
    std::cout << std::chrono::duration_cast<jiffies>(sec).count()
              << " jiffies\n";
    std::cout << microfortnights(sec).count() << " microfortnights\n";
    std::cout << nanocenturies(sec).count() << " nanocenturies\n";
}
程式執行效果如下:
1 second is:
100000000 shakes
100 jiffies
0.82672 microfortnights
0.316957 nanocenturies
基於duration,等待一個期望35毫秒的實現如下:
std::future<int> f=std::async(some_task);

if(f.wait_for(std::chrono::milliseconds(35))==std::future_status::ready)
	do_something_with(f.get());
等待函式會返回一個狀態來標示是超時了還是等待的時間發生了。如例子所示,等待的是一個期望,如果超時了,返回值為std::future_status::timeout,如果時間發生了,返回值為std::future_status::ready。

基於duration的等待機制使用的是穩定時鐘。