Здравствуйте, Andrew S, Вы писали:
__>>Однако Lock/Unlock пришлось сделать виртуальными. __>>В C++11 можно было бы просто сделать __>>
__>>auto const& lockScope = LockScope(cs);
__>>
__>>И тогда у lockScope был бы конкретный тип.
__>>А как можно это решить в рамках C++03 ?
AS>В поставленном ключе, на мой взгляд, задача легальным способом силами С++ 03 не решается. Все сводится к необходимости изобрести свой TYPEOF. Что обычно имеет тенденцию не работать на различных полуэкзотических компиляторах.
Придется ускорить переход на C++11
AS>Лучше сделать как в бусте. Тайпдефом — LockObject::ScopedLock. AS>На первый взгляд это выглядит не сильно лучше ScopedLock<LockObject>, но на деле позволяет писать трейты, не задумываясь о пригодности ScopedLock для данного объекта.
Все равно тип писать нужно, в общем, нужно переходить на С++11.
Здравствуйте, _nn_, Вы писали:
__>Однако Lock/Unlock пришлось сделать виртуальными. __>В C++11 можно было бы просто сделать __>
__>auto const& lockScope = LockScope(cs);
__>
__>И тогда у lockScope был бы конкретный тип.
__>А как можно это решить в рамках C++03 ?
__>P.S. __>Желательно без BOOST_TYPEOF
Хорошо бы выяснить, точно ли это надо решать.
Дело в том, что например в таком вот коде:
void foo( MyLock& l )
{
ConcreteScopeLocker<MyLock> locker( l );
locker.Unlock();
locker.Lock()
}
нет никаких виртуальных вызовов, так как тип объекта locker заведомо известен компилятору. По идее, и тип временного объекта, время жизни которого мы продлеваем ссылкой, тоже известен компилятору. Было бы странно, если бы компилятор это дело не оптимизировал и делал бы виртуальные вызовы. Хорошо бы проверить, делает он их или нет в каждом конкретном случае...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Можно вместо виртуальности использовать указатели на функции.
Плюс — избавляемся от лишних классов (можем хоть на лету конструировать свои скоупгарды на все случаи жизни).
Минусы — просадка в скорости, трудности оптимизатору, трудности отладчику (невнятное содержимое Watch).
typedef function<void()> voidfunc; // boost:: или std::tr1:: по вкусуclass ScopeLocker
{
mutable voidfunc enter_, leave_;
public:
ScopeLocker() {}
ScopeLocker(voidfunc enter, voidfunc leave) { enter(); enter_ = enter; leave_ = leave; } // если в enter исключение, то до присваивания не дойдём
ScopeLocker(const ScopeLocker& src) { swap(enter_, src.enter_); swap(leave_, src.leave_); } // эстафетное копирование, ибо!
~ScopeLocker() { if(leave_) leave_(); }
void Lock() { if(enter_) enter_(); }
void Unlock() { if(leave_) leave_(); }
};
ScopeLocker concreteLock(ConcreteMutex& m) { return ScopeLocker(bind(&ConcreteMutex::Lock,&m), bind(&ConcreteMutex::Unlock,&m)); }
Причём можно под конкретную задачу облегчить устройство этих voidfunc — заменив на шаблонную свободную функцию и указатель
Но вообще, мне кажется плохой затеей вытаскивать пару методов Lock/Unlock наружу. Это шанс для несбалансированного вызова.
Я больше скажу: рекурсивные мьютексы — рассадник зла. Даже в сочетании с условными переменными, делающими unlock на время ожидания. Особенно с условными переменными.
Потому что
— мы делаем unlock и думаем, что больше нет повода для взаимоблокировки (а где-то выше по стеку есть действующий lock)
— мы делаем lock и думаем, что вплоть до следующего unlock никто не вмешается (а где-то в недрах есть unlock-wait-lock)
А избавившись в ScopeLocker от необходимости вытаскивать методы, — возвращаемся к тому самому ad-hoc-auto, которое уже сделано.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте!
Привет! К> ScopeLocker(const ScopeLocker& src) { swap(enter_, src.enter_); swap(leave_, src.leave_); } // эстафетное копирование, ибо!
ой боюсь, боюсь!
К>Но вообще, мне кажется плохой затеей вытаскивать пару методов Lock/Unlock наружу. Это шанс для несбалансированного вызова.
К>Я больше скажу: рекурсивные мьютексы — рассадник зла. Даже в сочетании с условными переменными, делающими unlock на время ожидания. Особенно с условными переменными. К>Потому что К>- мы делаем unlock и думаем, что больше нет повода для взаимоблокировки (а где-то выше по стеку есть действующий lock) К>- мы делаем lock и думаем, что вплоть до следующего unlock никто не вмешается (а где-то в недрах есть unlock-wait-lock)
эти "потому что" валидны для случаев, когда руками вызывают Lock\Unlock
если воспитать себя и использовать только AutoLock и только на стеке (см. мой первый ответ), то все гораздо радужнее будет
рекурсивность мьютексов усложняет анализ работы программы, однако вносит дополнительные бенефиты. так что тут палка о двух концах
лично мне рекурсивность еще не портила жизнь, а только упрощала (потому что проги пишу прямолинейные и тормозные )
Нечего тут бояться!
Дело в том, что, когда мы ад-хок-автопеременные вводим,
Some x = Some(a,r,g,s);
Some const& y = Some(a,r,g,s);
Some const& z = Derived(a,r,g,s);
Some const& t = evaluated(a,r,g,s);
то, формально, это инициализация копированием
Some x( Some(a,r,g,s) );
Some y( Some(a,r,g,s) );
Derived shadow1( Derived(a,r,g,s) ); Some const& z = shadow1;
Evaluated shadow2(Evaluated(evaluated(a,r,g,s))); Some const& t = shadow2;
Компилятор имеет право сократить лишние конструкторы копирования, но может это и не сделать — особенно, в последнем случае.
Поэтому у нас выбор
— или передавать эстафету владения (права на unlock)
— или делать рекурсивный lock
чтобы количество unlock'ов в деструкторах всех промежуточных объектов совпадало с количеством lock'ов.
Эстафету, опять-таки, можно делать явно (как у меня), или неявно (используя трюки с shared_ptr).
U>эти "потому что" валидны для случаев, когда руками вызывают Lock\Unlock U>если воспитать себя и использовать только AutoLock и только на стеке (см. мой первый ответ), то все гораздо радужнее будет U>рекурсивность мьютексов усложняет анализ работы программы, однако вносит дополнительные бенефиты. так что тут палка о двух концах U>лично мне рекурсивность еще не портила жизнь, а только упрощала (потому что проги пишу прямолинейные и тормозные )
Пока критические секции кода — действительно секции и действительно критические (т.е. маленькие и атомарные), то да.
(Тем более нет нужды вытаскивать наружу методы lock/unlock у scope guard'а!)
А если мы пишем монитор (на паре из мьютекса и условной переменной), в котором встречаются {lock — {unlock-wait-lock} — unlock}, то рекурсия противопоказана.