sharedの使い方ptrロックの競合を減らす

2939 ワード

同時環境下でのロックの使用は日常茶飯事であり、どのようにロックの使用を減らすかはプログラム性能を最適化する一態様である.c++11にスマートポインタstd::shared_が追加されましたptr、これは私たちにヒントを与えるかもしれません.shared_ptrの1つの特性は、参照カウントが0の場合、その持つスタックメモリが自動的に解放されることである.この機能を利用して、以下のプログラムで実用的な機能を作ることができます.
#include <assert.h>
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>

std::shared_ptr<int> kNumPtr(new int(0));
std::mutex kmtx;

std::shared_ptr<int> getSharedPtr()
{
        kmtx.lock();
        std::shared_ptr<int> ptr = kNumPtr;
        kmtx.unlock();
        return ptr;
}

void dosomething(std::shared_ptr<int> ptr)
{
        std::cout << "value: " << *ptr << std::endl;
}

int main()
{

        auto threadProc = [&](){
                        for(size_t i = 0; i < 100; ++i)
                        {
                                kmtx.lock();
                                if(!kNumPtr.unique()){
                                        kNumPtr.reset(new int(*kNumPtr));
                                }
                                assert(kNumPtr.unique());
                                *kNumPtr = *kNumPtr + 1;
                                kmtx.unlock();
                                std::this_thread::sleep_for(std::chrono::milliseconds(1));
                        }
                };

        std::thread t1(threadProc);
        std::thread t2(threadProc);
        std::thread t3(
                [&](){
                        for(size_t i = 0; i < 100000; ++i)
                        {
                                std::shared_ptr<int> ptr = getSharedPtr();
                                dosomething(ptr);
                        }
                }
        );

        t1.join();
        t2.join();
        t3.join();

        std::cout << "kNumPtr's value: " << *kNumPtr << std::endl;
        assert(*kNumPtr = 200);
        assert(kNumPtr.unique());
}

私たちは全部で3つのスレッドを起動して、このプログラムのシミュレーションのシーンは読む頻度が書く頻度よりはるかに大きいです.2つの書き込みスレッドは共有データの修正をシミュレートし、1つの読み取りスレッドは高周波読み取りの行為をシミュレートする.dosomethingはデータの操作をシミュレートする.通常、私たちのプログラムは読み取り操作時に鍵をかける必要がありますが、ここではコピースマートポインタの操作に鍵をかけるだけで、臨界領域の長さは基本的に無視できます.dosomethingの時間が長い場合、例えばサーバネットワークプログラミングで通常のIO読み書きであれば、このロックのオーバーヘッドは実際には大きく、他のスレッド共有リソースへのアクセス性能が大幅に遅延します.現在のコードに置き換えると、その代価はほとんど無視できる.
書き込みスレッドを見てみると、書き込みスレッドにはcopyが使われています.on_writeテクノロジーは、古い共有リソースを新しいアドレス空間にコピーする上で、この古いリソースがresetの後に参照カウント(use_count)の値が0であることが重要であり、リードスレッドで処理が完了すると、オペレーティングシステムが自動的に古いメモリを解放する点が本当に興味深い.
プログラムアドレス:https://github.com/xiaopeifeng/CodeTricks/blob/master/shared_ptr_copyonwrite.cc