Использование std::condition_variable с пользовательским (вращением) мьютексом?

Я столкнулся с трудностями при использовании std::condition_variable с моей собственной реализацией вращающегося мьютекса. Похоже, что std::condition_variable ожидает, что std::mutex будет связан с его блокировкой, что приводит к ошибкам компиляции при попытке использовать другую реализацию мьютекса.

Вот упрощенная версия моего кода, в которой возникает ошибка компиляции:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

class SpinMutex {
public:
    void lock() {
        // Implement spin lock logic here
    }

    void unlock() {
        // Implement spin unlock logic here
    }

private:
    // Implement atomic flag or other spin lock mechanism here
};

SpinMutex mutex;
std::condition_variable cv;
bool ready = false;

void worker_thread() {
    // Do some work...
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // Attempting to lock a SpinMutex with std::lock_guard
    // This line will throw a compilation error
    std::unique_lock<SpinMutex> lock(mutex);

    ready = true;
    cv.notify_one();
}

int main() {
    std::thread worker(worker_thread);

    {
        std::unique_lock<SpinMutex> lock(mutex);
        cv.wait(lock, [] { return ready; });
    }

    worker.join();
    return 0;
}

Как показано, использование std::unique_lock с SpinMutex вызывает ошибку компиляции. Похоже, что std::condition_variable тесно связан с std::mutex, что делает его несовместимым с пользовательскими реализациями мьютекса, такими как спин-мьютекс.

Думаю, можно использовать std::condition_variable_any, но у меня вопрос: почему стандарт решил пойти таким путем? Это из-за устаревших целей или потому, что они реализуют специальную оптимизацию для std::mutex. Другими словами, для негениального разработчика, например. SpinMutex выше, могу ли я гарантировать, что std::mutex превзойдет мою собственную реализацию в целом?

Захват реализации VS

🤔 А знаете ли вы, что...
C++11, C++14, C++17 и C++20 - это различные версии стандарта C++, каждая из которых внесла свои улучшения.


100
1

Ответ:

Решено

Думаю, мне пригодится std::condition_variable_any

Да исправить.

но мой вопрос: почему стандарт решил пойти таким путем?

Цель API синхронизации в C++ — предоставить переносимую оболочку API синхронизации ОС. А на дизайн API C++ большое влияние оказал API pthreads Linux.

Таким образом, std::mutex и std::condition_variable должны быть максимально тонкими оболочками базового API ОС. С точки зрения pthreads, std::mutex сопоставляется с pthread_mutex_t. И std::condition_variable сопоставляется с pthread_cond_t.

И pthread_cond_t ждет pthread_mutex_t. Но в то время стало понятно, что:

  1. Разумно хотеть ждать чего-то кроме std::mutex, и
  2. Ожидание обобщенной концепции мьютекса будет дороже, чем std::mutex.

Таким образом, std::condition_variable_any был разработан для удовлетворения потребностей тех, кто хочет дождаться обобщенной концепции мьютекса, не заставляя тех, кому эта общность не нужна, платить за эту функцию.

Вот ранняя статья с обоснованием дизайна:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#condition

То, что в этой статье называется cond_var, в конечном итоге было переименовано в condition_variable. И то, что в этой статье называется gen_cond_var, в конечном итоге было переименовано в condition_variable_any.

Также следует отметить: в этой статье подробно рассказывается о сложности реализации gen_cond_var. Однако в реализации этой статьи все еще существовала ошибка. Это было сложнее, чем тогда предполагали даже авторы. Однако, насколько мне известно, все основные реализации std::condition_variable_any, начиная с C++11, не содержат ошибок.