创建一个锁,以保留C++ 11中锁定尝试的顺序

iol*_*olo 13 c++ multithreading locking c++11

有没有办法确保被阻塞的线程以被阻止的顺序被唤醒?我在某处读到这将被称为"强锁",但我没有找到任何资源.

在Mac OS X上,可以设计一个FIFO队列来存储被阻塞线程的所有线程ID,然后使用nifty函数pthread_cond_signal_thread_np()唤醒一个特定线程 - 这显然是非标准和非可移植的.

我能想到的一种方法是使用一个类似的队列,并在unlock()此时向broadcast()所有线程发送一个并让它们检查哪一个是下一个.
但这会产生很多开销.

解决问题的方法是将packaged_task发送到队列并让它按顺序处理它们.但对我而言,这似乎更像是一种解决方案而非解决方案.

编辑:
正如评论所指出的,这个问题可能听起来无关紧要,因为原则上没有保证锁定尝试的顺序.
作为澄清:

我有一些我称之为ConditionLockQueue的东西,它与Cocoa库中的NSConditionLock类非常相似,但是它维护了一个被阻塞的线程的FIFO队列而不是一个或多或少的随机池.

基本上任何线程都可以"排队"(有或没有特定'条件'的要求 - 一个简单的整数值 - 要满足).然后将该线程放在队列上并阻塞,直到它成为满足条件的队列中最前面的元素.

这提供了一种非常灵活的同步方式,我发现它在我的程序中非常有用.
现在我真正需要的是一种唤醒具有特定id的特定线程的方法.
但这些问题几乎是相似的.

Chr*_*odd 14

很容易构建一个使用编号票证的锁定对象,以确保其完全公平(锁定是在线程首先尝试获取它的顺序中授予的):

#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();
    }
};
Run Code Online (Sandbox Code Playgroud)

编辑

修复奥拉夫的建议:

#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) {
            cvar.emplace();
            cvar.back().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();
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 第二个版本在虚假唤醒的情况下不起作用(等待有时可能会继续).另外,我想知道在另一个等待线程继续之前调用cvar.pop()时是否存在问题.然后条件变量被销毁,而其他线程仍然等待该变量触发...... (4认同)
  • @Quirliom:使用较新版本的 C++,您需要 `emplace_back` 而不是 `push_back` 来避免移动/复制,但旧版本没有 `emplace_back` (2认同)
  • 为了避免虚假唤醒和破坏条件变量,只需: 1. 将 `cvar.pop()` 移至 `lock` 函数。2. 不再可能使用计数器变量,但仍然可以存储指向最近唤醒的条件变量的指针,并让“lock”函数与其进行比较。 (2认同)