SleepEx
MSDNを引用させてもらうと、SleepExは
現在のスレッドを中断します。次の条件のいずれかが満たされると、実行を再開します。
•I/O 完了コールバック関数が呼び出された。
•非同期プロシージャコール(APC)オブジェクトが、スレッドの ACP キューに置かれた。
•タイムアウト時間が経過した。
とのことである。
マルチスレッド処理などで定期的に何かをしたい時に、Sleepを使ってそれを行うという方法があるが、たまに途中でSleepを中断したい時がある。そのような場合にはこちらの方がよい。さて中断する方法であるが、上の引用の通りI/O 完了コールバック関数(ReadFileExやWriteFileEx)やAPCオブジェクトがおかれた場合となっている。
QueueUserAPC
こちらもMSDNを引用させてもらって
ユーザーモード非同期プロシージャ呼び出し(APC)オブジェクトを、指定したスレッドの APC キューに追加します。
ところで、このユーザーモードは何かというと、Windowsにはカーネルモードとユーザーモードがある。一般ユーザーが扱えるメモリ・リソースなどが動いているモードがユーザーモードというわけである。つまり安全なソフトウェア割り込みをする仕組みがこのAPC(Asynchronous Procedure Call)なのである。
さて、先ほどのSleepExはAPCオブジェクトがおかれた場合抜け出すことができるとのことであったが、QueueUserAPCによりそのオブジェクトを作成することができる。
パフォーマンス測定
さて、上記SleepExとQueueUserAPCのコンビネーションにかかる演算量をrdtsc(Read Time Stamp Counter)で測ってみた。
コード的には、アラーと可能なSleepExと
1 |
SleepEx( INFINITE, TRUE ); |
何の中身のない関数を呼ぶQueueUserAPCである
1 |
QueueUserAPC( EmptyUserAPC, hThread, dwData ); |
これを100回試行してその平均値と中央値を取ってみた。結果は次の通り。
平均値 | 36614 |
中央値 | 21879 |
さて、これがどのぐらい演算に影響するものなのかというと実際大したことはないということがわかる。次の空のループと同じぐらいの演算コストである。これが大体21400ぐらいであった。
1 2 3 |
for( volatile int m = 0; m < 3000; m++ ) { } |
ちなみに、Sleep( 1000 )としてrdtscを測ると2399873221.8となりほぼマシンの周波数(2.4GHz)と同じになる。
ピンバック: Safe Device デッドロックの回避とマルチスレッド化 | 豆知識