16

ブロックされたスレッドがブロックされたのと同じ順序でウェイクアップされるようにする方法はありますか?これは「強力なロック」と呼ばれることをどこかで読みましたが、そのリソースは見つかりませんでした。

Mac OS Xでは、ブロックされたスレッドのすべてのスレッドIDを格納するFIFOキューを設計し、nifty関数を使用しpthread_cond_signal_thread_np()て1つの特定のスレッドをウェイクアップできます。これは明らかに非標準で移植性がありません。

私が考えることができる1つの方法は、同様のキューを使用し、そのunlock()時点ですべてのスレッドにを送信しbroadcast()て、次のスレッドをチェックすることです。
しかし、これは多くのオーバーヘッドを引き起こします。

この問題を回避する方法は、packaged_taskをキューに発行し、それらを順番に処理することです。しかし、それは解決策というよりは回避策のように思えます。

編集:
コメントで指摘されているように、ロックの試行の順序は原則として保証されていないため、この質問は無関係に聞こえるかもしれません。
明確化として:

CocoaライブラリのNSConditionLockクラスに非常によく似たConditionLockQueueと呼ぶものがありますが、多かれ少なかれランダムなプールではなく、ブロックされたスレッドのFIFOキューを維持します。

基本的に、どのスレッドも「整列」できます(特定の「条件」(単純な整数値)の要件の有無にかかわらず)。次に、スレッドはキューに配置され、条件が満たされたキューの最前部の要素になるまでブロックされます。

これは非常に柔軟な同期方法を提供し、プログラムで非常に役立つことがわかりました。
今私が本当に必要とするのは、特定のIDで特定のスレッドをウェイクアップする方法です。
しかし、これらの問題はほとんど同じです。

4

3 に答える 3

17

番号付きチケットを使用して完全に公正であることを保証するロック オブジェクトを作成するのは非常に簡単です (ロックはスレッドが最初にロックを取得しようとした順序で付与されます)。

#include <mutex>
#include <condition_variable>

class ordered_lock {
    std::condition_variable  cvar;
    std::mutex               cvar_lock;
    unsigned int             next_ticket, counter;
public:
    ordered_lock() : next_ticket(0), counter(0) {}
    void lock() {
        std::unique_lock<std::mutex> acquire(cvar_lock);
        unsigned int ticket = next_ticket++;
        while (ticket != counter)
            cvar.wait(acquire);
    }
    void unlock() {
        std::unique_lock<std::mutex> acquire(cvar_lock);
        counter++;
        cvar.notify_all();
    }
};

編集

オラフの提案を修正するには:

#include <mutex>
#include <condition_variable>
#include <queue>

class ordered_lock {
    std::queue<std::condition_variable *> cvar;
    std::mutex                            cvar_lock;
    bool                                  locked;
public:
    ordered_lock() : locked(false) {};
    void lock() {
        std::unique_lock<std::mutex> acquire(cvar_lock);
        if (locked) {
            std::condition_variable signal;
            cvar.emplace(&signal);
            signal.wait(acquire);
        } else {
            locked = true;
        }
    }
    void unlock() {
        std::unique_lock<std::mutex> acquire(cvar_lock);
        if (cvar.empty()) {
            locked = false;
        } else {
            cvar.front()->notify_one();
            cvar.pop();
        }
    }
};
于 2013-02-09T23:10:05.517 に答える
-1

このスレッドで正しい質問をしていますか??? もしそうなら、彼らは正しく答えられていますか???

または別の言い方をすると:

私はここで物事を完全に誤解していますか??

段落を編集: StatementOnOrder (以下を参照) が false のようですlink1 ( Linux での C++ スレッドなどは多くの場合、pthreads に基づいています) とlink2 (決定要因として現在のスケジューリング ポリシーに言及しています) を参照してください - cppreference ( ref ) の Cubbi に感謝します。リンクリンクリンクリンクも参照してください。ステートメントが false の場合は、以下のコードに示すように、アトミック (!) チケットをプルする方法がおそらく優先されます!!

ここに行きます...

StatementOnOrder : 「ロックされたミューテックスに実行され、特定の順序で「スリープ状態になる」複数のスレッドは、その後ミューテックスの所有権を取得し、同じ順序で続行します。

質問: StatementOnOrderはtrue または falseですか???

void myfunction() {
    std::lock_guard<std::mutex> lock(mut);

    // do something
    // ...
    // mutex automatically unlocked when leaving funtion.
}

このページのこれまでのすべてのコード例は、次のいずれかのように見えるため、これを尋ねています。

a) 無駄 ( StatementOnOrderが true の場合)

また

b) 重大な間違い ( StatementOnOrderが false の場合)。

では、 StatementOnOrderが falseの場合、なぜ「深刻に間違っている」可能性があると言うのでしょうか?
その理由は、すべてのコード例は を利用することで非常にスマートであると考えていますがstd::condition_variable、実際にはその前にロックを使用しているため、( StatementOnOrderが false の場合) 順序が台無しになります!!! 皮肉なことに、
このページで を検索してください。std::unique_lock<std::mutex>

したがって、StatementOnOrderが本当に false の場合、ロックに遭遇することはなく、その後チケットや condition_variables を処理することはできません。代わりに、次のようなことを行う必要があります:ロックに遭遇する前にアトミック チケットを取得します!!! ロックに遭遇する前に
、なぜチケットを引くのですか? ここではStatementOnOrderが false であると想定しているため、順序付けは「悪」ロックの前に行う必要があります。

#include <mutex>
#include <thread>
#include <limits>
#include <atomic>
#include <cassert>
#include <condition_variable>
#include <map>

std::mutex mut;
std::atomic<unsigned> num_atomic{std::numeric_limits<decltype(num_atomic.load())>::max()};
unsigned num_next{0};
std::map<unsigned, std::condition_variable> mapp;

void function() {
    unsigned next = ++num_atomic; // pull an atomic ticket

    decltype(mapp)::iterator it;

    std::unique_lock<std::mutex> lock(mut);
    if (next != num_next) {
        auto it = mapp.emplace(std::piecewise_construct,
                               std::forward_as_tuple(next),
                               std::forward_as_tuple()).first;
        it->second.wait(lock);
        mapp.erase(it);
    }



    // THE FUNCTION'S INTENDED WORK IS NOW DONE
    // ...
    // ...
    // THE FUNCTION'S INDENDED WORK IS NOW FINISHED

    ++num_next;

    it = mapp.find(num_next); // this is not necessarily mapp.begin(), since wrap_around occurs on the unsigned                                                                          
    if (it != mapp.end()) {
        lock.unlock();
        it->second.notify_one();
    }
}

上記の関数は、プルされたアトミック チケットに従って注文が実行されることを保証します。(編集:ブーストの侵入型マップを使用して、(ローカル変数として) スタックに condition_variable を保持することは、フリーストアの使用を減らすためにここで使用できる優れた最適化です! )

しかし、主な質問は次のとおりです。StatementOnOrderはtrue ですか、それとも false ですか?
(もしそれが本当なら、上記の私のコード例も無駄であり、mutex を使用するだけで完了できます。) Anthony Williamsのような誰かがこのページをチェックしてくれること
を願っています... ;)

于 2016-06-12T16:57:26.833 に答える