『Muduo C++ネットワークライブラリ学習一』Observerモードでスレッドセキュリティ問題について

3709 ワード

これは私がmuduoライブラリを学ぶ第2段階で、前の段階は主にネットワークライブラリの部分のソースコードを読んで、大体ネットワーク通信の各部分の詳細を理解しました.スレッドセキュリティを記述するクラスは難しいことではなく,同期原語(反発量,条件変数,信号量など)で内部状態を保護すればよい.しかし、対象の生と死は対象自身のmutexによって保護されない(△).オブジェクト解析時に存在する競合条件(race condition)を回避する方法は、C++マルチスレッドプログラミングが直面する基本的な問題であり、Boostライブラリのshared_が正しい答えです.ptrとweak_ptr(C++11に標準ライブラリを追加).このBlogの執筆目的は「Linuxマルチスレッドサービス端プログラミング」の第1章を読んだ後の総括と体得であり、無駄に見ないようにすることである.これからは
  • Observer設計モデルと組み合わせて、競争条件(race condition)と、なぜ前の(△)という言葉を話します.
  • 概要shared_ptrとweak_ptr、それらがどのようにこの問題を解決したのかについて話します.

  • 競合条件
      C++はプログラマーにオブジェクトのライフサイクルを自分で管理するように要求するため,マルチスレッドでは特に困難である.特に、1つのオブジェクトが複数のスレッドで同時に表示される場合、いわゆる競合条件(race condition):
  • オブジェクトをプロファイルしようとすると、オブジェクトのメンバー関数が現在実行されている別のスレッドがあるかどうかをどのように知ることができますか?
  • は、メンバー関数の実行中にオブジェクトが別のスレッドで析出しないことをどのように保証しますか?
  • オブジェクトのメンバー関数を呼び出す前に、このオブジェクトがまだ生きていることをどのように知るか.その構造関数はたまたま半分に実行されますか?

  • Observerモードでrace conditionを見てみましょう.Observerモードとは、オブザーバーモードとも呼ばれ、一対多の設計モデルであり、Observable(オブザーバー)が変化すると、それに関連付けられたObserver(オブザーバー)がアクティブに更新され、コードは以下の通りである.
    //   
    class Observer {
    public:
    	~Observer();
    	void update();
    };
    //    
    class Obervable {
    public:
    	void notifyObservers() {
    		for (Observer* x : observers_) {
    			x->update();
    		}
    	}
    private:
    	std::vector observers_;
    };
    

    ターゲットの変化を観察し、notifyObserversを呼び出すと、Observerオブジェクトがまだ生きていることをどのように知ることができますか?1つの方法は、Observer構造関数でObservable::unregister()関数を呼び出して登録を解除することですが、まだ問題があります.この場合のObservableのオブジェクトが有効であることを確認するにはどうすればいいですか?あるスレッドがObserverを構築しているのに、別のスレッドがx->update()を呼び出してObserverを更新している場合、どのような結果が得られますか?したがって、現在呼び出したいオブジェクトがまだ生きているかどうかを教えてくれるメンバー関数isAlive()がほしいです.
    次に(△)問題について話します.反発ロックメンバー変数を追加すると、各スレッドはオブジェクトにアクセスするときにロックの使用権を取得してからアクセスを続行します.よさそうですが、ロックが有効であることが前提ですが、構造関数ではmutexメンバー変数を破棄するという仮定が破壊されます.別のスレッドがロックの使用権を待っていた場合、次に何が起こるかは予測できません.
    インテリジェントポインタ
    Muduoネットワークライブラリのソースコードのほとんどがスマートポインタ方式で元のポインタを回避しているのは、現代のC++のプログラミングスタイルかもしれません.ソースコードには3つのスマートポインタが必要です:shared_ptr,weak_ptr,scoped_ptr.shared_ptrは強引用であり、その大まかな使い方は以下の通りである.
  • 『C++Primer』第12章ダイナミックメモリ
  • を読む
  • boost公式ドキュメントを読むhttps://www.boost.org/doc/libs/1_68_0/libs/smart_ptr/doc/html/smart_ptr.html#shared_ptr
  • Baidu他の人が書いたBlogのいくつかの方法はこれらの方法を理解して、個人は学習の優先度が減少していると思っています.weak_ptrは弱い参照であり、コピー制御時にその参照カウントを増加させない.また、現在のオブジェクトを指すshared_を取得したい場合はptrオブジェクト、enabled_を使用できますshared_from_this.

  • 最後に、スマートポインタをObserverに適用した例を見てみましょう.以前の説明によれば、Observableは依存するObserverを保存するためにvectorを必要とし、Observerオブジェクトの弱い参照を保存し、更新時に弱い参照を取り出し、参照が有効であれば更新し、そうでなければvectorから削除します.コードは以下の通りです.
    void Observable::notifyObservers() {
    //std::vector> observers_;
    	for (auto iter = observers_.begin(); iter != obervers_.end(); ) {
    		shared_ptr obj = iter->lock(); // weak_ptr::lock(),     ,     ;     shared_ptr
    		if (obj) {
    			obj->update;
    			iter++;
    		} else {
    			iter = obervers_.erase(iter); //             
    		}
    	}
    }
    

    まとめ
      shared_ptrは共有リソースを管理する利器であり、循環参照を避けることに注意する必要があります.通常、ownerはchildを指すshared_を持っています.ptr,childはownerを指すweakを持っているptr.オブジェクトがまだ生きている場合は、メンバー関数を呼び出す必要があります.そうしないと無視されます.この場合、通常は「弱いコールバック」テクノロジーで解決されます.弱いコールバックとはweak_を使うことですptr、コールバック時にshared_にアップグレードしてみますptrは、アップグレードに成功した場合にコールバックを実行し、そうでなければコールバックを実行しません.コールバックについて一般的なコールバック関数は、ユーザー自身が指定し、実行時に他の人のコードによって呼び出されます.このような方式は設計の柔軟性を極めて高めて、例えばC++STLの中のsort関数、ユーザーはcmp関数をカスタマイズして自分の“小さい”規則を定義することができて、ライブラリの設計者はユーザーがどのように設計することに関心を持つ必要はなくて、それはこのインタフェースの形状だけを規定してokになりました.TcpConnection接続が作成されるとコールバックconnectionCallback_が呼び出されるなど、多くのコールバックがMuduoネットワークライブラリに採用されています.を選択します.またMuduoライブラリには大量のboost::bind()とboost::function()を適用してコールバック関数をバインドしていますが、現在はこの2つの使い方の理解がまだ深くないので、後で勉強するにつれて徐々に理解していきましょう.