2

オブジェクトへのスレッド セーフなアクセスにこのクラス ラッパーを使用できますか?また、望ましい動作は C++11 に準拠していますか?

弦への主なアクセント:

T* operator->() {

T& operator*() {

ここでは整数(int)に std::atomic<> を使用するのが最適であることはわかっていますが、このコードでは int の代わりに他のオブジェクトを使用できます。

バージョン 2.0 では、execute-around ポインター イディオムを使用します。

#include<thread>
#include<mutex>
#include<memory>
#include<iostream>
#include<vector>

template<typename T>
class safe_obj {
    T obj;
    mutable std::mutex mtx;
    safe_obj(safe_obj<T> &) {}
    safe_obj<T>& operator=(safe_obj<T> &) {}

    class T_exclusive_lock {
         std::unique_lock<std::mutex> xlock;
    public:
         T*const self;
         T_exclusive_lock(T * const s, std::mutex& _mtx)
             :self(s), xlock(_mtx) {}

        T* operator -> () const {return self;}
        operator T&()  {return *self;}

        friend std::ostream& operator << (std::ostream &stream, T_exclusive_lock &obj) {
            stream << obj;
            return stream;
        }
    };

public:
    template<typename  Types>
    safe_obj(Types  args) : obj(args ) 
    { }

    T_exclusive_lock operator->() {
        return T_exclusive_lock(&obj, mtx);
    }    

    T_exclusive_lock* operator*() {
        return &T_exclusive_lock(&obj, mtx);
    }       
};

int main() {
    safe_obj<std::shared_ptr<int> > safe_int( std::make_shared<int>(10) );   

    auto lambda = [&safe_int]() {
        std::cout << safe_int->use_count() << std::endl;    // is that thread-safe? 
        std::cout << *safe_int << std::endl;    // is that thread-safe? 
        std::cout << *safe_int->get() << std::endl;    // is that thread-safe? 
    };

    std::vector<std::thread> thr_grp;
    for(size_t i = 0; i < 10; ++i) thr_grp.emplace_back(std::thread(lambda));
    for(auto &i : thr_grp) i.join();

    int b; std::cin >> b;
    return 0;
}
4

3 に答える 3

8

あなたが提供した元のコードは、スレッドセーフを保証していませんでした。std::unique_lock保護したいオブジェクトを使用する前に、範囲外になるとすぐにミューテックスのロックを解除します。

目的の結果を得るには、別のテンプレート化されたクラス ( など) を宣言する必要があります。このクラスは、(のミューテックスにlocked_obj<T>を持つことによって) ロックされた状態のオブジェクトを表し、オーバーロードされた演算子からそのようなオブジェクトを返します。このようなオブジェクトは一時的なものであり、保護されたオブジェクトを の存続期間中に操作できます。一時オブジェクトは現在のステートメントの最後まで存続するため、そのようなs の使用はほとんど透過的です。unique_locksafe_objlocked_objsafe_obj

この手法は、execute-around ポインターイディオムを応用したものです。

于 2013-08-24T14:47:06.860 に答える
3

個々の関数をロックしたり、個々のオブジェクトへのアクセスをロックしたりしても、スレッド セーフは保証されません。プログラムによっては、多くの操作で、中断することなく同じオブジェクトに対して複数の関数を呼び出す必要があるか、中断することなく複数のオブジェクトに対して操作を行う必要があります。アプリケーションにスレッド セーフを設計する必要があります。適切に設計されていないアプリケーションをスレッドセーフにするようなライブラリ ハックはありません。

于 2013-08-24T17:37:57.293 に答える
2

いいえ、それはスレッドセーフではありません。アクセサー関数が戻るまでにミューテックスはロック解除されているため、オブジェクトへのアクセスは同期されません。

1 つの方法は、代入演算子と変換演算子をオーバーロードすることです。

safe_obj & operator=(T const &t) {
    std::unique_lock<std::mutex> lock(mtx);
    obj = t;
    return *this;
}

operator T() {
    std::unique_lock<std::mutex> lock(mtx);
    return obj;
}

しかし、すべての複合代入演算子を提供したい場合、これは面倒になる可能性があり、オブジェクトがコピー可能でない場合、変換演算子は機能しません。

別のアプローチは、 を含むアクセサ オブジェクトを返すことunique_lockです。これにより、オブジェクトにアクセスできる限りミューテックスがロックされたままになります。

于 2013-08-24T14:49:38.090 に答える