サーチ…
前書き
リソース取得が初期化される
RAII(Resource Acquisition Is Initialization)は、リソース管理における共通のイディオムです。動的メモリの場合、 スマートポインタを使用してリソース管理を行います。 RAIIを使用する場合、取得されたリソースは、スマートポインタまたは同等のリソースマネージャにすぐに所有権が与えられます。リソースはこのマネージャを介してのみアクセスされるため、マネージャはさまざまな操作を追跡できます。たとえば、 std::auto_ptr
は、スコープから外れるか、または削除されると、対応するリソースを自動的に解放します。
#include <memory>
#include <iostream>
using namespace std;
int main() {
{
auto_ptr ap(new int(5)); // dynamic memory is the resource
cout << *ap << endl; // prints 5
} // auto_ptr is destroyed, its resource is automatically freed
}
std::auto_ptr
の主な問題は、所有権を譲渡せずにコピーできないことです。
#include <memory>
#include <iostream>
using namespace std;
int main() {
auto_ptr ap1(new int(5));
cout << *ap1 << endl; // prints 5
auto_ptr ap2(ap1); // copy ap2 from ap1; ownership now transfers to ap2
cout << *ap2 << endl; // prints 5
cout << ap1 == nullptr << endl; // prints 1; ap1 has lost ownership of resource
}
これらの奇妙なコピーセマンティクスのために、 std::auto_ptr
は、とりわけコンテナでは使用できません。この理由は、メモリを2回削除することを防ぐためです。同じリソースの所有権を持つauto_ptrs
が2つある場合、それらは破壊されたときに解放しようとします。すでに解放されているリソースを解放すると一般的に問題が発生する可能性があるため、そのリソースを防ぐことが重要です。しかし、 std::shared_ptr
は、これを避け、コピー時に所有権を移さない方法があります:
#include <memory>
#include <iostream>
using namespace std;
int main() {
shared_ptr sp2;
{
shared_ptr sp1(new int(5)); // give ownership to sp1
cout << *sp1 << endl; // prints 5
sp2 = sp1; // copy sp2 from sp1; both have ownership of resource
cout << *sp1 << endl; // prints 5
cout << *sp2 << endl; // prints 5
} // sp1 goes out of scope and is destroyed; sp2 has sole ownership of resource
cout << *sp2 << endl;
} // sp2 goes out of scope; nothing has ownership, so resource is freed
ミューテックスとスレッドセーフティ
複数のスレッドがリソースにアクセスしようとすると、問題が発生する可能性があります。簡単な例として、変数にスレッドを追加するスレッドがあるとします。これは、最初に変数を読み込み、変数を1つ追加してから戻します。この変数を1に初期化し、このスレッドの2つのインスタンスを作成するとします。両方のスレッドが終了すると直感的に、この変数の値は3になるはずです。ただし、次の表には何が間違っているかが示されています。
スレッド1 | スレッド2 | |
---|---|---|
時間ステップ1 | 変数から1を読み込む | |
時間ステップ2 | 変数から1を読み込む | |
時間ステップ3 | 1プラス1を加えて2を得る | |
時間ステップ4 | 1プラス1を加えて2を得る | |
時間ステップ5 | 2を変数に格納する | |
時間ステップ6 | 2を変数に格納する |
ご覧のように、操作の終わりに2が変数に3ではなく変数に入っています。その理由は、スレッド1がスレッド1を更新する前に変数を読み取るためです。ソリューション?ミューテックス。
ミューテックス(MUT UAL の元 clusionのかばん)はこの種の問題を解決するために設計されたリソース管理対象です。スレッドがリソースにアクセスしたい場合、スレッドはリソースのミューテックスを「取得」します。リソースへのアクセスが完了すると、スレッドはミューテックスを「解放」します。ミューテックスが取得されている間、ミューテックスを取得するためのすべての呼び出しは、ミューテックスが解放されるまで戻りません。これをよりよく理解するには、スーパーマーケットで待ち行列としてミューテックスを考えてみましょう。ミューテックスを取得して先行するスレッドが終了するのを待ってから、リソースを使用してスレッドを終了します。ミューテックスをリリースしてください。誰もが一度にリソースにアクセスしようとすると、完全なパンデモニウムが存在します。
std::mutex
はC ++ 11のミューテックスの実装です。
#include <thread>
#include <mutex>
#include <iostream>
using namespace std;
void add_1(int& i, const mutex& m) { // function to be run in thread
m.lock();
i += 1;
m.unlock();
}
int main() {
int var = 1;
mutex m;
cout << var << endl; // prints 1
thread t1(add_1, var, m); // create thread with arguments
thread t2(add_1, var, m); // create another thread
t1.join(); t2.join(); // wait for both threads to finish
cout << var << endl; // prints 3
}