サーチ…


前書き

CおよびC ++で最も難しいことの1つは、リソース管理です。ありがたいことに、C ++では、私たちのプログラムでリソース管理を設計する多くの方法があります。この記事では、割り当てられたリソースを管理するために使用されるイディオムやメソッドのいくつかについて説明します。

リソース取得が初期化される

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
}
C ++ 11

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のかばん)はこの種の問題を解決するために設計されたリソース管理対象です。スレッドがリソースにアクセスしたい場合、スレッドはリソースのミューテックスを「取得」します。リソースへのアクセスが完了すると、スレッドはミューテックスを「解放」します。ミューテックスが取得されている間、ミューテックスを取得するためのすべての呼び出しは、ミューテックスが解放されるまで戻りません。これをよりよく理解するには、スーパーマーケットで待ち行列としてミューテックスを考えてみましょう。ミューテックスを取得して先行するスレッドが終了するのを待ってから、リソースを使用してスレッドを終了します。ミューテックスをリリースしてください。誰もが一度にリソースにアクセスしようとすると、完全なパンデモニウムが存在します。

C ++ 11

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
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow