std:: timed mutex:: try lock* неудачно

By try_lock*, Я имею в виду try_lock(), try_lock_for() и try_lock_until(). Согласно cppreference, все три метода могут просто потерпеть неудачу. Следующее цитируется из описания для try_lock_for()

С try_lock(), эта функция может завершиться неудачно и возвращение false даже если мьютекс не заблокирован другим потоком в какой-то момент во время timeout_duration.

Я знаю, что ложное пробуждение может произойти с std::condition_variable и ее обоснование. Но что происходит с мьютексом?

2 ответов


согласно:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3209.htm

с другой стороны, есть веские причины требовать, чтобы программы были написаны, чтобы терпеть ложные сбои try_lock ():

  1. как указано в Boehm, Adve, " основы модели памяти параллелизма C++", PLDI 08, обеспечение последовательной согласованности для программ без гонки данных без ложных сбоев try_lock () требует значительно более сильное упорядочение памяти для операций lock () на типах мьютексов, совместимых с try_lock (). На некоторых архитектурах это значительно увеличивает стоимость незапланированных приобретений мьютекса. Эта стоимость, по-видимому, значительно перевешивает любую выгоду от запрета ложных сбоев try_lock ().
  2. это позволяет пользователю, написанному try_lock (), потерпеть неудачу, если, например, реализация не может получить блокировку низкого уровня, используемую для защиты структуры данных мьютекса. Или позволяет записать такую операцию непосредственно в плане compare_exchange_weak.
  3. это гарантирует, что клиентский код остается правильным, когда, например, вводится поток отладки, который иногда получает блокировки, чтобы иметь возможность считывать согласованные значения из проверяемой или исследуемой структуры данных. Любой код, который получает информацию от сбоя try_lock (), будет нарушен с введением другого потока, который просто блокирует и считывает структуру данных.

из главы c++14 "30.4.1.2 типы мьютексов"

пункт 16:

реализация может не получить блокировку, даже если она не удерживается каким-либо другим потоком. [Примечание: этот паразитный сбой обычно необычен, но позволяет интересные реализации, основанные на простом сравнении и обмене (пункт 29). - end note] реализация должна гарантировать, что try_lock() не последовательно возвращать false при отсутствии конкурирующего мьютекса приобретения.

и пункт 19:

мало что было бы известно о состоянии после сбоя, даже при отсутствии ложных сбоев

и в ответ на

Я знаю, что ложное пробуждение может произойти с std:: condition_variable и ее обоснование. Но что происходит с мьютексом?

std::timed_mutex иногда реализуется с помощью std::condition_varible когда нет прямой поддержки в ОС. Как в GNU libstdc++:

#if _GTHREAD_USE_MUTEX_TIMEDLOCK

...

#else // !_GTHREAD_USE_MUTEX_TIMEDLOCK

  class timed_mutex
  {
    mutex       _M_mut;
    condition_variable  _M_cv;
    bool        _M_locked = false;

  public:

    template<typename _Rep, typename _Period>
      bool
      try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
      {
        unique_lock<mutex> __lk(_M_mut);
        if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
          return false;
        _M_locked = true;
        return true;
      }

    template<typename _Clock, typename _Duration>
      bool
      try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
      {
        unique_lock<mutex> __lk(_M_mut);
        if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
          return false;
        _M_locked = true;
        return true;
      }
  };

#endif