排他的制御を行いたい時に使うのがこのCiriticalSection(クリティカルセクション)。具体的にいつ使うのかというと、マルチスレッド処理などをしている時に、同時にアクセスされるとまずい部分(メモリ領域)がある場合に使う。
例えば、2つスレッドがあった時、一方のスレッドで配列のサイズを変更し、もう片方のスレッドで同じ配列のある場所を読み込む場合、配列サイズ変更中にアクセスするとアクセス違反が発生し、場合によってはアプリケーションが落ちることがある。
WindowsでこのCriticalSectionを使う場合は、InitializeCriticalSection、EnterCriticalSection、LeaveCriticalSection、DeleteCriticalSectionという関数及びCRITICAL_SECTIONという構造体を利用する。具体的使い方などは、MSDNなどで調べてもらうとして、今回はこれを使った場合の実行速度を調べてみたいと思う。
速度
速度は、単純なforループとCriticalSection付のループで調べてみた。具体的なコードは以下の通り(ループ回数は、48kHzで1時間分、最適化は無効)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const int end = 60 * 60 * 48000; for( int n = 0; n < end; n++ ) { } CRITICAL_SECTION CriticalSection; ::InitializeCriticalSection( &CriticalSection ); for( int n = 0; n < end; n++ ) { ::EnterCriticalSection( &CriticalSection ); ::LeaveCriticalSection( &CriticalSection ); } ::DeleteCriticalSection( &CriticalSection ); |
Intel Core 2 Duoのマシンで測定してみたが、何もないループが、0.515106秒に対しクリティカルセクションでは7.089556秒となった。単純に考えた場合、1ループが14倍遅くなっている(このコストをどう考えるかだが、信号処理なら1サンプルごとの処理などにこれを使うには向いていないだろう)。
また、これらの関数は、同じスレッドから2度呼んでも正常に動作してくれる。これは、ロックカウントがあるためである。
1 2 3 4 5 6 7 |
for( int n = 0; n < end; n++ ) { ::EnterCriticalSection( &CriticalSection ); ::EnterCriticalSection( &CriticalSection ); ::LeaveCriticalSection( &CriticalSection ); ::LeaveCriticalSection( &CriticalSection ); } |
この場合の測定した時間は13.841430秒となり、さらに倍弱になってしまったことからCriticalSectionをなるべくかぶらせない努力は若干必要である。