cv::AutoLock, cv::Mutex

この記事は[OpenCV Advent Calendar 2015](http://qiita.com/advent-calendar/2015/opencv)の6日目の記事です．

# はじめに
OpenCVでは排他制御に用いる以下の機能を提供しています．

* cv::AutoLock
* cv::Mutex

また，これらの機能はマルチプラットフォーム対応であるため，排他制御のコードを
プラットフォーム間で共通化できるというメリットもあります．

この記事ではこれらの機能と使い方を紹介します．

# cv::AutoLockクラス
cv::AutoLockクラスは，インスタンスが生存する間においてcv::Mutexクラスの
インスタンスを用いて排他制御を行う仕組みです．以下に疑似コードを紹介します．

```cpp
{
    cv::AutoLock lock(mutex); // mutexを使ってロックする
    counter++;

    // lockインスタンスが破棄される（=このスコープを出る）とアンロックする
}
```

AutoLockという名前の由来は，

* <code>cv::AutoLock</code>クラスのインスタンスを生成した時点でロックを行う
* <code>cv::AutoLock</code>クラスのインスタンスを破棄した時点でアンロックを行う

ことを内部的に自動で行うことから来ています．

使い方や機能は，C++やBoostで言うところの[std::lock_guard(C++)](http://en.cppreference.com/w/cpp/thread/lock_guard)や
[boost::mutex::scoped_lock(Boost)](http://www.boost.org/doc/libs/1_59_0/doc/html/boost/interprocess/scoped_lock.html)と似ているかもしれません．

## cv::AutoLockクラスの内部処理
[公式ドキュメント](http://docs.opencv.org/3.0.0/dd/de2/classcv_1_1AutoLock.html#aaee95814e8ba8929b595a7a26ae546e3)を読んで調べようと思ったのですがほとんど情報がありませんでした。。。orz

ということで，気を取り直して<code>modules/core/include/opencv2/core/utility.hpp</code>の
cv::AutoLock実装を読んでみます．

```cpp
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&);
};
```

上記のコードからもわかるように<code>cv::AutoLock</code>クラスは，

* クラスメンバとして<code>cv::Mutex</code>のインスタンスを持っており，
* コンストラクタで<code>cv::Mutex</code>のlockメソッドをコールし，
* デストラクタで<code>cv::Mutex</code>のunlockメソッドをコールしています．

ここで登場する<code>cv::Mutex</code>については次で説明します，

# cv::Mutexクラス
<code>cv::Mutex</code>は排他制御に用いるロック処理を担うクラスであり，
先の説明でも紹介したようにcv::AutoLock内部でも用いられています．

通常，排他制御に用いるロック処理はOSに依存するのですが，
OpenCVが提供する<code>cv::Mutex</code>はOSの違いを吸収しているため，
ユーザが環境依存のコードを書く必要がないようになっています．

以下に<code>cv::Mutex</code>が内部的に使用している機能をOS毎にまとめました．

| OS | 関数 |
|:--|:--|
| Windows | CriticalSection |
| Linux/MacOS | pthread |

詳細な実装を知りたい方は<code>modules/core/include/opencv2/core/utility.hpp</code>を参照ください．

# サンプルコード
以下に<code>cv::AutoLock</code>を用いて排他制御を行うサンプルコードを紹介します．

## サンプルコード（cv::AutoLock）
```cpp:autolock_test.cpp
#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;
}
```

また，<code>ENABLE_AUTOLOCK</code>をundefすると排他制御を行わないようになります．
興味のある方は実際に排他制御有り・無しで実行してみて結果の違いを見てみるとよいかもしれません．

# おわりに
OpenCVを使ったプログラミングにおいてマルチプラットフォーム対応の排他制御処理を実装する際に

* cv::AutoLock
* cv::Mutex

を使用するという選択肢もあります．

# 備考
筆者は以下の環境で動作確認しました．

* [OpenCV 3.0.0](https://github.com/Itseez/opencv/releases/tag/3.0.0)
* Windows 8.1 Pro（64bit）
* Visual Studio 2013 Update5