3

Anthony Williams がチャプター 6.2.3 C++ Concurrency in Action で紹介したきめの細かいロック キューを次に示します。

/*
    pop only need lock head_mutex and a small section of tail_mutex,push only need
    tail_mutex mutex.maximum container concurrency.
*/
template<typename T> class threadsafe_queue
{
    private:
    struct node
    {
        std::shared_ptr<T> data;
        std::unique_ptr<node> next;
    }
    std::mutex head_mutex;   //when change the head lock it.
    std::unique_ptr<node> head;  
    std::mutex tail_mutex;   //when change the tail lock it.
    node* tail;
    std::condition_variable data_cond;

    node* get_tail()
    {
        std::lock_guard<std::mutex> tail_lock(tail_mutex);
        return tail;
    }

    public:
    /* 
        create a dummy node
    */
    threadsafe_queue():
        head(new node),tail(head.get())
    {}

    std::shared_ptr<T> wait_and_pop()
    {
        std::unique_lock<std::mutex> head_lock;
        data_cond.wait(head_lock,[&]{return head.get()!=get_tail();}); //#1
        std::unique_ptr<node> old_head=std::move(head);
        head=std::move(old_head->next);
        return old_head;
    }

    void push(T new_value)
    {
        std::shared_ptr<T> new_data(
        std::make_shared<T>(std::move(new_value)));
        std::unique_ptr<node> p(new node);
        {
            std::lock_guard<std::mutex> tail_lock(tail_mutex);
            tail->data=new_data;
            node* const new_tail=p.get();
            tail->next=std::move(p);
            tail=new_tail;
        }
        data_cond.notify_one();
    }
}

状況は次のとおりです。2 つのスレッド (thread1およびthread2) があります。thread1を行っておりwait_and_popthread2を行っていpushます。キューは空です。

thread1は #2 にあり、head.get()!=get_tail()以前にチェック済みdata_cond.wait()です。この時点で、その CPU 期間は終了していました。thread2始まります。

thread2push機能を終えましたdata_cond.notify_one()thread1再び始まります。

thread1始まりますがdata_cond.wait()、それは永遠に待ちます。

この状況が発生する可能性はありますか?その場合、このコンテナを修正するにはどうすればよいですか?

4

2 に答える 2

7

はい、OP に記載されている状況が発生する可能性があり、通知が失われる可能性があります。述語関数に大きな時間遅延を挿入すると、トリガーが簡単になります。これはColiruでのデモンストレーションですwait_forプログラムが完了するまでに 100 ミリ秒 (プロデューサーが項目をキューに挿入する時間) ではなく、10 秒 (までのタイムアウトの長さ) を要していることに注意してください。通知が失われます。

関連するミューテックスがロックされている間は、条件の状態 (述語の戻り値) を変更できないという条件変数の設計には暗黙の前提があります。pushを保持せずにキューの「空」を変更できるため、これはこのキューの実装には当てはまりませんhead_mutex

§30.5p3 は、wait3 つのアトミック パーツがあることを指定します。

  1. ミューテックスの解放、および待機状態へのエントリ。
  2. 待機のブロック解除; と
  3. ロックの再取得。

に渡された場合、これらのいずれも述語のチェックについて言及していないことに注意してくださいwait。述語を使用したの動作はwait、§30.5.1p15 で説明されています。

効果:

ながら (!pred())
      待機(ロック);

ここでも、述語チェックとwaitがアトミックに実行されるという保証はないことに注意してください。ロックされている前提条件がありlock、呼び出しスレッドによって保持されている関連付けられたミューテックスです。

通知が失われないようにコンテナーを修正する限り、単一のミューテックス実装に変更して、それで完了です。pushいずれにせよとpopの両方が同じミューテックス ( ) を取得することになる場合、それをきめの細かいロックと呼ぶのは少し無理が​​ありtail_mutexます。

于 2013-08-01T08:15:45.223 に答える