Компромисс между рекурсивными мьютексами V более сложный класс? - PullRequest
2 голосов
/ 23 августа 2010

Я слышал, что рекурсивные мьютексы - это зло , но я не могу придумать лучший способ заставить этот класс работать без него.

class Object {
public:
    bool check_stuff() {
        lock l(m);

        return /* some calculation */;
    }
    void do_something() { 
        lock l(m);

        if (check_stuff()) {
            /* ... */
        }
        /* ... */
    }
private:
    mutex m;
}

Функция do_something() блокируется, если m не является рекурсивной, но какая альтернатива, если у меня нет, скажем, двух способов выполнения проверки, один из которых не блокируется.

Этот выбор, как предполагает ссылка выше, в конечном итоге сделает класс более сложным, есть ли более точное решение, которое не требует рекурсивного мьютекса? Или рекурсивный мьютекс иногда не , что зло?

Ответы [ 2 ]

4 голосов
/ 23 августа 2010

Если вам нужно выставить check_stuff как общедоступный (а также do_something, который в данный момент вызывает его), простой рефакторинг проведет вас без повторов каких-либо вычислений:

class Object {
public:
    bool check_stuff() {
        lock l(m);

        return internal_check_stuff(l);
    }
    void do_something() { 
        lock l(m);

        if (internal_check_stuff(l)) {
            /* ... */
        }
        /* ... */
    }
private:
    mutex m;
    bool internal_check_stuff(lock&) {
        return /* some calculation */;
    }
}

(согласно@ Ответ Логана, передача ссылки на блокировку внутренней функции - это просто хорошая идея, чтобы избежать забывчивости от будущих сопровождающих; -).

Рекурсивные блокировки необходимы только при вызове метода, который блокирует другой, который такжеделает (с той же блокировкой), но с небольшим рефакторингом (перемещение общей функциональности в частные методы, которые не блокируют, а принимают ссылки на блокировку как «напоминания» ;-) устраняет необходимость.

Нет необходимости повторятьлюбую функциональность - просто сделайте открытые функции, которые предоставляют точно такую ​​же (блокирующую) функциональность, которая также необходима (неблокирующим образом, потому что блокировка уже получена) из других функций, в «полупустые оболочки», которые выполняют блокировку, затем вызывают такиечастные функции, которые выполняют всю необходимую работу.Другие функции в классе, которые нуждаются в такой функциональности, но хотят явно выполнять собственную блокировку, могут просто вызывать частные функции напрямую.

0 голосов
/ 23 августа 2010

Сделайте check_stuff приватным, а не блокированным, и просто вызовите его из do_something с заблокированным мьютексом.Обычная идиома, используемая для повышения безопасности, заключается в передаче ссылки на блокировку в check_stuff:

 bool check_stuff(const lock&)
 {
    return /* some calculation */;
 }

 void do_something()
 {
    lock l(m);
    if(check_stuff(l)) { // can't forget to lock, because you need to pass it in to call check_stuff
      ...
    }
 }

check_stuff не должен блокировать и возвращать логическое значение в любом случае, любое возвращаемое значение устареловремя, когда вы смотрите на него, делая его довольно бесполезным для потребителей, не имеющих доступа к замку.

...