Re: Временный блокировщик
От: Кодт Россия  
Дата: 06.02.24 13:26
Оценка: 9 (2)
Здравствуйте, Marty, Вы писали:

M>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.

M>В других потоках, или в том же потоке, можно запрещать, рекурсивно, главное, разлочить столько же раз, сколько и залочил.

Если не вдаваться в подробности, то правда ли, что апи получается такой? Назовём эту штуку "дверь". Потому что поток проходит через неё и дальше занимается своими делами.
struct Door {
  int level = 0;

  bool is_open() const { return level > 0; }

  // апи управляющей стороны (не ждёт) (меняет состояние двери)
  void open() { ++level; }
  void close() { --level; }

  // апи управляемой стороны (не меняет состояние двери)
  bool try_pass() const { return is_open(); }  // не ждёт
  bool pass(auto timeout) const { WAIT(timeout, is_open); return is_open(); }
  void pass() const { WAIT(is_open); }
}


Надо заметить, что поток может запереть сам себя:
void foo(Door& door) {
  door.close();
  .....
  door.pass();
  .....
}

и в этом случае ему нужна внешняя помощь — какой-то другой поток должен отпереть эту дверь

M>Что-то городить с какими-то примитивами синхронизации не вижу смысла, ну, может я ошибаюсь.


Примитивы синхронизации стоят дёшево, пока не попадают на блокировку.
Но если очень хочется упороться по лок-фри и написать своё, заведомо userspace, то ради бога.

Семафоры и мьютексы, будучи симметричными инструментами, тут не подойдут. (Не, конечно, на паре семафоров можно построить абсолютно все производные примитивы).
А вот на кондеях такое делается элементарно
class Door {
  int level = 0;
  mutable std::mutex guard;
  std::conditional_variable cv;

  bool is_open() const {
    return level > 0;
  }
  auto is_open_fun() const {
    return [this]() { return is_open(); };
  }

public:
  void open() {
    std::unique_lock lock(guard);
    ++level;  // спасибо vopl за фикс недочепятки
    if (is_open()) cv.notify_all();
  }
  void close() {
    std::unique_lock lock(guard);
    --level;
  }
    
  bool try_pass() const {
    std::unique_lock lock(guard);
    return is_open();
  }
  bool pass(auto timeout) const {
    std::unique_lock lock(guard);
    return cv.wait_for(lock, timeout, is_open_fun());
  }
  void pass() const {
    std::unique_lock lock(guard);
    cv.wait(lock, is_open_fun());
  }
};
Перекуём баги на фичи!
Отредактировано 06.02.2024 18:24 Кодт . Предыдущая версия . Еще …
Отредактировано 06.02.2024 18:24 Кодт . Предыдущая версия .
Отредактировано 06.02.2024 18:22 Кодт . Предыдущая версия .
Отредактировано 06.02.2024 13:48 Кодт . Предыдущая версия .
Re[5]: Временный блокировщик
От: reversecode google
Дата: 04.02.24 19:06
Оценка: +1
у нас есть, у тебя хз
https://en.cppreference.com/w/cpp/header/semaphore
Re[2]: Временный блокировщик
От: vopl Россия  
Дата: 06.02.24 13:57
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>А вот на кондеях такое делается элементарно

К>
К>class Door {
К>  int level = 0;
К>  mutable std::mutex guard;
К>  std::conditional_variable cv;

К>  bool is_open() const {
К>    return level > 0;
К>  }
К>  auto is_open_fun() const {
К>    return [this]() { return is_open();
К>  }

К>public:
К>  void open() {
К>    std::unique_lock lock(guard);
    // тут недочепятка, надо еще ++level; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
К>    if (is_open()) cv.notify_all();
К>  }
К>  void close() {
К>    std::unique_lock lock(guard);
К>    --level;
К>  }
    
К>  bool try_pass() const {
К>    std::unique_lock lock(guard);
К>    return is_open();
К>  }
К>  bool pass(auto timeout) const {
К>    std::unique_lock lock(guard);
К>    return cv.wait_for(lock, timeout, is_open_fun());
К>  }
К>  void pass() const {
К>    std::unique_lock lock(guard);
К>    cv.wait(lock, is_open_fun());
К>  }
К>};
К>
Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.02.24 07:07
Оценка:
Здравствуйте!


Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.

В других потоках, или в том же потоке, можно запрещать, рекурсивно, главное, разлочить столько же раз, сколько и залочил.

Что-то городить с какими-то примитивами синхронизации не вижу смысла, ну, может я ошибаюсь.

Пока придумалась такая схема (псевдокод):

class Locable
{
   std::atomic<bool>  m_lock;

public:
   void lock();
   void unlock();
   bool isLocked() const;
};

// Где-то в цикле что-то периодически делается

if (!locablePermitter.isLocked())
{
    doSomething();
}

// Тут ведём подсчет количества блокировок

class Locker
{
    std::atomic<int>  m_counter;
    Locable          *m_pLocable;

public:

    void lock()
    {
        if (++m_counter > 0)
        {
            m_pLocable->lock();
        }
    }

    void lock()
    {
        if (--m_counter <= 0)
        {
            m_pLocable->unlock();
        }
    }

};



Ну или как лучше сделать? Может, есть что-то стандартное, чем изобретать велосипеды?
Маньяк Робокряк колесит по городу
Re: Временный блокировщик
От: andrey.desman  
Дата: 04.02.24 07:16
Оценка:
Здравствуйте, Marty, Вы писали:


M>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.


Просто пропустить, вывешивать в ожидание не надо?

M>Пока придумалась такая схема (псевдокод):

M>Ну или как лучше сделать? Может, есть что-то стандартное, чем изобретать велосипеды?

Если тормозить поток на нем не надо, то такая схема ок. Разве что два атомика тут лишние. Один класс lockable со счетчиком, лочить через std::lock_guard.
Re[2]: Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.02.24 07:23
Оценка:
Здравствуйте, andrey.desman, Вы писали:

M>>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.


AD>Просто пропустить, вывешивать в ожидание не надо?


Да, именно так


M>>Пока придумалась такая схема (псевдокод):

M>>Ну или как лучше сделать? Может, есть что-то стандартное, чем изобретать велосипеды?

AD>Если тормозить поток на нем не надо, то такая схема ок. Разве что два атомика тут лишние. Один класс lockable со счетчиком, лочить через std::lock_guard.


Лишний атомик в булевском флаге, я правильно понял?
Маньяк Робокряк колесит по городу
Re[2]: Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.02.24 07:25
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Просто пропустить, вывешивать в ожидание не надо?


Ну и я, наверное, не совсем правильно тут использовал слова на базе lock, может как-то по другому назвать?
Маньяк Робокряк колесит по городу
Re: Временный блокировщик
От: andrey.desman  
Дата: 04.02.24 07:25
Оценка:
Здравствуйте, Marty, Вы писали:

M>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.


Хотя не понятно, что значит запрещать? Если поток уже что-то делает запрещенное, а ты запретил, это будет ок? Или нужна критсекция все-таки?
Наверное тебе подойдет std::shared_mutex.
Re: Временный блокировщик
От: vopl Россия  
Дата: 04.02.24 07:26
Оценка:
Здравствуйте, Marty, Вы писали:

M>Ну или как лучше сделать? Может, есть что-то стандартное, чем изобретать велосипеды?


У тебя получился Семафор
Re[2]: Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.02.24 07:27
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Здравствуйте, Marty, Вы писали:


M>>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.


AD>Хотя не понятно, что значит запрещать? Если поток уже что-то делает запрещенное, а ты запретил, это будет ок?


Нормас, главное, чтобы потом в следующий раз пропустил
Маньяк Робокряк колесит по городу
Re[2]: Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.02.24 07:29
Оценка:
Здравствуйте, vopl, Вы писали:

M>>Ну или как лучше сделать? Может, есть что-то стандартное, чем изобретать велосипеды?


V>У тебя получился Семафор



Семафор блокирующий примитив
Маньяк Робокряк колесит по городу
Re[3]: Временный блокировщик
От: vopl Россия  
Дата: 04.02.24 07:32
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, vopl, Вы писали:


M>>>Ну или как лучше сделать? Может, есть что-то стандартное, чем изобретать велосипеды?


V>>У тебя получился Семафор



M>Семафор блокирующий примитив

У тебя он тоже блокирующий, только блокирует не поток а семантику. Это всего лишь вопрос точки зрения. Но, хозяин барин, пусть это не Семафор
Re[2]: Временный блокировщик
От: reversecode google
Дата: 04.02.24 15:54
Оценка:
шаред мютекс проседает на любых тестах по сравнении с обычным мютексом

даже доклад на какой то конфе было
где чел там пытался переизобретать что то даже на атомиках что бы ускориться
Re: Временный блокировщик
От: reversecode google
Дата: 04.02.24 16:37
Оценка:
std::recursive_mutex не оно?
Re[4]: Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.02.24 18:59
Оценка:
Здравствуйте, vopl, Вы писали:

M>>Семафор блокирующий примитив

V>У тебя он тоже блокирующий, только блокирует не поток а семантику. Это всего лишь вопрос точки зрения. Но, хозяин барин, пусть это не Семафор

Да пусть и семафор

Окай, семафор, где у нас есть проверяющий неблокирующий семафор?
Маньяк Робокряк колесит по городу
Re[2]: Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.02.24 19:06
Оценка:
Здравствуйте, reversecode, Вы писали:

R>std::recursive_mutex не оно?


Он же рекурсивен только для захватившего потока, для остальных блокирует, не?
Маньяк Робокряк колесит по городу
Re: Временный блокировщик
От: tryAnother  
Дата: 05.02.24 08:13
Оценка:
Чтобы не потерять разблокировку, удобнее использовать raii, а не руками вызывать Lock (если код конечно позволяет)
тогда счетчик можно перенести в класс Locable.


class Locker;

class Locable
{
    friend Locker;
    std::atomic<int> m_counter;
  
public:
    Locable(): m_counter(0) {}
    operator bool () const {return !m_counter; }
};

class Locker
{
    Locker(const Locker&) = delete;
    Locker& operator=(const Locker&) = delete;
    
    Locable & m_locable;
public:
    Locker(Locable & l):m_locable(l) { ++m_locable.m_counter; }
    ~Locker() {--m_locable.m_counter;}
};
Отредактировано 05.02.2024 14:07 tryAnother . Предыдущая версия .
Re[2]: Временный блокировщик
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.02.24 09:10
Оценка:
Здравствуйте, tryAnother, Вы писали:

A>Чтобы не потерять разблокировку, удобнее использовать raii, а не руками вызывать Lock (если код конечно позволяет)


Это высшая магия, естественно, я её буду использовать. Когда будет надо. Например, в скопах
Маньяк Робокряк колесит по городу
Re: Временный блокировщик
От: Chorkov Россия  
Дата: 05.02.24 13:50
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте!



M>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.


M>В других потоках, или в том же потоке, можно запрещать, рекурсивно, главное, разлочить столько же раз, сколько и залочил.


M>Что-то городить с какими-то примитивами синхронизации не вижу смысла, ну, может я ошибаюсь.

...

M>Ну или как лучше сделать? Может, есть что-то стандартное, чем изобретать велосипеды?


shared_mutex:

class Locable
{
   mutable std::shared_mutex  m_mutex;

public:
   void lock() { m_mutex.lock_shared(); }
   void unlock() { m_mutex.unlock_shared(); }
   bool isLocked() const {
        if( ! m_mutex.try_lock() ) 
             return true;
        m_mutex.unlock();
        return true;
   }
};
Отредактировано 05.02.2024 13:54 Chorkov . Предыдущая версия .
Re[2]: Временный блокировщик
От: reversecode google
Дата: 05.02.24 13:59
Оценка:
https://rsdn.org/forum/cpp.applied/8683879.1
Автор: reversecode
Дата: 04.02.24
Re[2]: Временный блокировщик
От: Кодт Россия  
Дата: 06.02.24 12:51
Оценка:
Здравствуйте, vopl, Вы писали:

V>У тебя получился Семафор


Не получился, потому что у семафора семантика "поднял где угодно кто угодно" / "опустил и ждёшь".
А тут — один поднял-опустил без блокировки, другой пытается пролезть и ждёт.
Перекуём баги на фичи!
Re[3]: Временный блокировщик
От: vopl Россия  
Дата: 06.02.24 12:58
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, vopl, Вы писали:


V>>У тебя получился Семафор


К>Не получился, потому что у семафора семантика "поднял где угодно кто угодно" / "опустил и ждёшь".

К>А тут — один поднял-опустил без блокировки, другой пытается пролезть и ждёт.

Ну, это если смотреть со стороны его реализации в конкретных схемах вытесняющего многопотока. Чуть абстрактнее если подняться, то семафор это "... счётчик, над которым можно производить две атомарные операции: увеличение и уменьшение значения на единицу, при этом операция уменьшения для нулевого значения счётчика является блокирующейся...". Вот по такой семантике у него вполне семафор, ноль как граница блокируемости, выше ноля свободно, ноль и ниже — заблокировано (или наоборот, не важно). Хотя, это все вопрос трактовки, спорить не стану, просто поясняю свой ход мыслей.
Re[2]: Временный блокировщик
От: reversecode google
Дата: 06.02.24 13:30
Оценка:
std::latch / std::barrier ?
Re[3]: Временный блокировщик
От: Кодт Россия  
Дата: 06.02.24 13:48
Оценка:
Здравствуйте, reversecode, Вы писали:

R>std::latch / std::barrier ?


Это примитивы для сетей Петри.
Прохождение через барьер всегда сбрасывает его состояние. Не факт, что нужно именно такое поведение.
Перекуём баги на фичи!
Re[4]: Временный блокировщик
От: Кодт Россия  
Дата: 06.02.24 13:54
Оценка:
Здравствуйте, vopl, Вы писали:

V>Ну, это если смотреть со стороны его реализации в конкретных схемах вытесняющего многопотока. Чуть абстрактнее если подняться, то семафор это "... счётчик, над которым можно производить две атомарные операции: увеличение и уменьшение значения на единицу, при этом операция уменьшения для нулевого значения счётчика является блокирующейся...". Вот по такой семантике у него вполне семафор, ноль как граница блокируемости, выше ноля свободно, ноль и ниже — заблокировано (или наоборот, не важно). Хотя, это все вопрос трактовки, спорить не стану, просто поясняю свой ход мыслей.


Семафор — счётчик расхода ресурсов. Его нельзя просто так отпереть так, что "работайте все кто хочет сколько хочет".
И его нельзя рекурсивно запереть так, что сам остался на свободе.

Ну и как строительный кирпич — семафор люто неудобная штука.
Перекуём баги на фичи!
Re[3]: Временный блокировщик
От: Maniacal Россия  
Дата: 06.02.24 14:08
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, reversecode, Вы писали:


R>>std::recursive_mutex не оно?


M>Он же рекурсивен только для захватившего потока, для остальных блокирует, не?


А данные в том потоке, в который иногда можно динамить, не те ли используются, что в других потоках? Не нужно ли лочить мьютекс в этом потоке так, чтобы другие подождали?
А так std::shared_mutex(C++17) или std::shared_timed_mutex(C++14). Для гурьбы потоков использовать lock_shared/unlock_shared, и try_lock для потока, который динамят
Но это не точно. Я такую разновидность мьютексов ещё не использовал. Что там с рекурсивностью — х.з.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.