Visual C++ 2012, 2013, 2015 の std::this_thread::sleep_for
関数は、内部で std::this_thread::sleep_until
関数を呼び出す実装になっています。
具体的に言うと、 sleep_for
の引数に渡された時間値を現在時刻に加算することで絶対時刻に変換し、それを sleep_until
関数の引数に渡しています。*1
この実装方法には問題があって、 sleep_for
関数の処理途中に別スレッドや別プロセスによって時刻が変更されると正常に動作しません。
例えば現在のPC時刻が 10時0分0秒 で、 sleep_for
関数の引数に 1秒 を渡し、その処理途中にユーザがPC時刻を 9時0分0秒 に変更したとすると、次のように動作します。
sleep_for
関数内で現在のPC時刻 10時0分0秒 と引数値 1秒 を加算して絶対時刻 10時0分1秒 を作成。sleep_until
関数には 10時0分1秒 が渡される。実際にこのような挙動になることを確認するためのサンプルコードは下記の通りです。
なお、 SetSystemTime
関数を成功させるために管理者権限で実行する必要があります。*2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| - - | ! | | | - - ! | | - ! | - ! - - ! | | - | ! | | - - ! | ! | - - ! | ! | - ! ! ! | - ! - - ! | - | ! ! | | ! |
|
コード内のコメントにも書いた通り、このサンプルでは1年間スリープすることになってしまい、プログラムが実質フリーズしてしまいます。
プログラム内では別スレッドからの時刻変更をしないように注意すればいいとしても、ユーザ操作による時刻設定を抑制するのは厳しいです。
当面は、素直に Windows API の Sleep
関数等を使っておくのが無難かもしれません。
なお、gccで使われているlibstdc++や、clang/LLVMで使われているlibc++における sleep_for
関数は、引数の時間値を時刻に変換したりせずそのままシステムのスリープ関数*3に渡す作りになっているため、VC++のような問題は起きません。
sleep_until
関数を直接呼び出した場合は同等の問題が起きる可能性がありますが、この関数は元々特定時刻まで待機するための関数なので、用途外のことに使わない限りはただ単に書いた通りに動くというだけの話でしょう。