18

リーダー/ライターロックのブーストライブラリ(バージョン1.45)をチェックしていました。テストを実行したとき、shared_ptrがリーダースレッドを優先しているように見えました。つまり、ライターがその操作のためにロックを取得しようとしたときに、後続の読み取りの発生が停止しませんでした。

ブーストでこの動作を変更することは可能ですか?

using namespace std;
using namespace boost;

mutex outLock;
shared_mutex workerAccess;
bool shouldIWork = true;

class WorkerKiller
{
public:   

    void operator()()  
    {
        upgrade_lock<shared_mutex> lock(workerAccess); 
        upgrade_to_unique_lock<shared_mutex> uniqueLock(lock);

        cout << "Grabbed exclusive lock, killing system" << endl;
        sleep(2);
        shouldIWork = false;
        cout << "KILLING ALL WORK" << endl;  
    }  

private:  
};

class Worker
{  
public:   

    Worker()
    {  
    }  

    void operator()()  
    {
        shared_lock<shared_mutex> lock(workerAccess); 

        if (!shouldIWork) {
            outLock.lock();
            cout << "Workers are on strike.  This worker refuses to work" << endl;
            outLock.unlock();
        } else {
            sleep(1);

            outLock.lock(); 
            cout << "Worked finished her work" << endl;
            outLock.unlock(); 
        }
    }  
};  

int main(int argc, char* argv[])  
{  
    Worker w1;
    Worker w2;
    Worker w3;
    Worker w4;
    WorkerKiller wk;

    boost::thread workerThread1(w1);
    boost::thread workerThread2(w2);

    boost::thread workerKillerThread(wk);

    boost::thread workerThread3(w3);
    boost::thread workerThread4(w4);

    workerThread1.join();
    workerThread2.join();
    workerKillerThread.join();
    workerThread3.join();

    return 0;
}

そして、これが毎回の出力です:

仕事は彼女の仕事を
終えた仕事は彼女の仕事を終えた仕事は彼女の仕事を終えた
仕事
は彼女の仕事を終えた
排他的なロックをつかみ、システムを殺す
すべての仕事を殺す

私の要件

ライターが排他ロックを取得しようとした場合は、以前のすべての読み取り操作を終了してください。そして、その後のすべての読み取り操作をブロックします。

4

2 に答える 2

33

私はこの質問に少し遅れていますが、私はいくつかの適切な情報を持っていると思います。

shared_mutexBoostlibsが基づいているC++委員会への提案は、読者にもライターにも優先権を与えるAPIを意図的に指定していませんでした。これは、AlexanderTerekhovが約10年前に完全に公正なアルゴリズムを提案したためです。これにより、オペレーティングシステムは、ミューテックスを取得する次のスレッドがリーダーかライターかを判断でき、オペレーティングシステムは、次のスレッドがリーダーかライターかを完全に認識しません。

このアルゴリズムにより、リーダーとライターのどちらを優先するかを指定する必要がなくなります。私の知る限り、ブーストライブラリはこの公正なアルゴリズムで実装されています(ブースト1.52)。

Terekhovアルゴリズムは、読み取り/書き込みミューテックスを2つのゲート(gate1とgate2)で構成することで構成されます。各ゲートを通過できるのは、一度に1つのスレッドのみです。ゲートは、ミューテックスと2つの条件変数を使用して実装できます。

リーダーとライターの両方がgate1を通過しようとします。gate1を通過するには、ライタースレッドが現在gate1の内部にないことが真である必要があります。存在する場合、gate1ブロックを通過しようとしているスレッド。

リーダースレッドがgate1を通過すると、ミューテックスの所有権が読み取られます。

ライタースレッドがgate1を通過するとき、ミューテックスの書き込み所有権を取得する前に、gate2も通過する必要があります。gate1内のリーダーの数がゼロになるまでgate2を通過できません。

gate1内にリーダーが0個以上しかない場合、gate1内に入る次のスレッドがリーダーかライターかはOS次第であるため、これは公正なアルゴリズムです。ライターは、gate1を通過した後にのみ「優先」され、ミューテックスの所有権を取得するために次に並んでいます。

最終的にC++14になったものの実装例に対してコンパイルされた例を使用しましたshared_timed_mutex(例に若干の変更を加えました)。以下のコードshared_mutexは、それが提案されたときに持っていた名前であると呼んでいます。

次の出力が得られました(すべて同じ実行可能ファイルで):

時々:

Worked finished her work
Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK
Workers are on strike.  This worker refuses to work
Workers are on strike.  This worker refuses to work

そして時折:

Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK
Workers are on strike.  This worker refuses to work
Workers are on strike.  This worker refuses to work
Workers are on strike.  This worker refuses to work

そして時折:

Worked finished her work
Worked finished her work
Worked finished her work
Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK

実験的には確認していませんが、理論的には他のアウトプットも得られるはずだと思います。

完全に開示するために、私が実行した正確なコードは次のとおりです。

#include "../mutexes/shared_mutex"
#include <thread>
#include <chrono>
#include <iostream>

using namespace std;
using namespace ting;

mutex outLock;
shared_mutex workerAccess;
bool shouldIWork = true;

class WorkerKiller
{
public:   

    void operator()()  
    {
        unique_lock<shared_mutex> lock(workerAccess); 

        cout << "Grabbed exclusive lock, killing system" << endl;
        this_thread::sleep_for(chrono::seconds(2));
        shouldIWork = false;
        cout << "KILLING ALL WORK" << endl;  
    }  

private:  
};

class Worker
{  
public:   

    Worker()
    {  
    }  

    void operator()()  
    {
        shared_lock<shared_mutex> lock(workerAccess); 

        if (!shouldIWork) {
            lock_guard<mutex> _(outLock);
            cout << "Workers are on strike.  This worker refuses to work" << endl;
        } else {
            this_thread::sleep_for(chrono::seconds(1));

            lock_guard<mutex> _(outLock);
            cout << "Worked finished her work" << endl;
        }
    }  
};  

int main()  
{  
    Worker w1;
    Worker w2;
    Worker w3;
    Worker w4;
    WorkerKiller wk;

    thread workerThread1(w1);
    thread workerThread2(w2);

    thread workerKillerThread(wk);

    thread workerThread3(w3);
    thread workerThread4(w4);

    workerThread1.join();
    workerThread2.join();
    workerKillerThread.join();
    workerThread3.join();
    workerThread4.join();

    return 0;
}
于 2012-11-19T00:06:17.360 に答える
1

「ブースト共有ロック飢餓」のグーグル検索はこのリンクを見つけました:

「アップグレード」が鍵になるようです。参照:

于 2012-08-22T22:47:40.733 に答える