この記事はOpenCV Advent Calendar 2015の6日目の記事です.
はじめに
OpenCVでは排他制御に用いる以下の機能を提供しています.
- cv::AutoLock
- cv::Mutex
また,これらの機能はマルチプラットフォーム対応であるため,排他制御のコードを
プラットフォーム間で共通化できるというメリットもあります.
この記事ではこれらの機能と使い方を紹介します.
cv::AutoLockクラス
cv::AutoLockクラスは,インスタンスが生存する間においてcv::Mutexクラスの
インスタンスを用いて排他制御を行う仕組みです.以下に疑似コードを紹介します.
{
cv::AutoLock lock(mutex); // mutexを使ってロックする
counter++;
// lockインスタンスが破棄される(=このスコープを出る)とアンロックする
}
AutoLockという名前の由来は,
-
cv::AutoLock
クラスのインスタンスを生成した時点でロックを行う -
cv::AutoLock
クラスのインスタンスを破棄した時点でアンロックを行う
ことを内部的に自動で行うことから来ています.
使い方や機能は,C++やBoostで言うところのstd::lock_guard(C++)や
boost::mutex::scoped_lock(Boost)と似ているかもしれません.
cv::AutoLockクラスの内部処理
公式ドキュメントを読んで調べようと思ったのですがほとんど情報がありませんでした。。。orz
ということで,気を取り直してmodules/core/include/opencv2/core/utility.hpp
の
cv::AutoLock実装を読んでみます.
class CV_EXPORTS AutoLock
{
public:
AutoLock(Mutex& m) : mutex(&m) { mutex->lock(); }
~AutoLock() { mutex->unlock(); }
protected:
Mutex* mutex;
private:
AutoLock(const AutoLock&);
AutoLock& operator = (const AutoLock&);
};
上記のコードからもわかるようにcv::AutoLock
クラスは,
- クラスメンバとして
cv::Mutex
のインスタンスを持っており, - コンストラクタで
cv::Mutex
のlockメソッドをコールし, - デストラクタで
cv::Mutex
のunlockメソッドをコールしています.
ここで登場するcv::Mutex
については次で説明します,
cv::Mutexクラス
cv::Mutex
は排他制御に用いるロック処理を担うクラスであり,
先の説明でも紹介したようにcv::AutoLock内部でも用いられています.
通常,排他制御に用いるロック処理はOSに依存するのですが,
OpenCVが提供するcv::Mutex
はOSの違いを吸収しているため,
ユーザが環境依存のコードを書く必要がないようになっています.
以下にcv::Mutex
が内部的に使用している機能をOS毎にまとめました.
OS | 関数 |
---|---|
Windows | CriticalSection |
Linux/MacOS | pthread |
詳細な実装を知りたい方はmodules/core/include/opencv2/core/utility.hpp
を参照ください.
サンプルコード
以下にcv::AutoLock
を用いて排他制御を行うサンプルコードを紹介します.
サンプルコード(cv::AutoLock)
#include <opencv2/core.hpp> // coreモジュールにて定義
#include <thread>
#include <mutex>
#include <vector>
#include <iostream>
#define ENABLE_AUTOLOCK
cv::Mutex g_mutex; // cv::Mutexクラスのインスタンス
int g_counter = 0; // カウンタ
void dummyProcessing(void)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
void incrementCounter(void)
{
dummyProcessing();
#ifdef ENABLE_AUTOLOCK
cv::AutoLock lock(g_mutex);
#endif
g_counter++;
}
int main(int argc, const char* argv[])
{
// このサンプルではスレッド数は500
std::vector<std::thread> threads(500);
// 各スレッドにてカウンタを増やす
for(auto& thread : threads)
{
thread =
std::thread(incrementCounter);
}
for(auto& thread : threads){ thread.join(); }
// この時点でカウンタは500になっているはず
std::cout << g_counter << std::endl;
return 0;
}
また,ENABLE_AUTOLOCK
をundefすると排他制御を行わないようになります.
興味のある方は実際に排他制御有り・無しで実行してみて結果の違いを見てみるとよいかもしれません.
おわりに
OpenCVを使ったプログラミングにおいてマルチプラットフォーム対応の排他制御処理を実装する際に
- cv::AutoLock
- cv::Mutex
を使用するという選択肢もあります.
備考
筆者は以下の環境で動作確認しました.
- OpenCV 3.0.0
- Windows 8.1 Pro(64bit)
- Visual Studio 2013 Update5