Здравствуйте, BitField, Вы писали:
>... так что мне не страшно
Может и не страшно...
Но часов 10 МУЧИТЕЛЬНЕЙШЕЙ ОТЛАДКИ было потрачено.
>А почем это так бьет -- по стлю, что ли?
ПЬЕСА "Кошмар программиста"
ужастик в трёх действиях без пролога(не язык) но с эпилогом.
Действие первое:
(if you cannot have the best, make the best of what you have)
Если в первом акте на стене висит ружьё
то в последнем оно выстрелит!(ну очень вольный перевод)
Рядовая контора взялась за многопоточный проект.
Блокировки частенько осуществлялись примерно так:
class lock_resource{...};
...
lock_resource(X), X.do_something();
Способ на троечку с плюсом, но прост как грабли.
Вот эти грабли и по лбу...
Действие второе:
(if you are tired then you'd better stay at home)
Увольнялся человек из конторы. (умница наверно был)
Зарплату последнюю не платили. (зачем? увольняется же)
Плюнул он на зарплату и постарался
сделать
super_puper_inline operator,()
что-бы даже в DEBUG-ере туда не попадать.
А ежели и попал то выглядит как безобидное
протоколирование блокировок...
Действие третье:
(to swim like a stone)
Враг вступает в город пленных не щадя.
Потому что в кузнице небыло гвоздя!(ещё более вольный перевод)
Недели через две народ заметил неладное.
Приложение злобно глючило!
Потоки, взявшись за руки, водили хороводы
и время от времени безбожно рвали в клочья общие ресурсы!
Ещё пару недель всем миром искали ошибки.
Сначала искали использование ресурсов без блокировки.
Потом ошибки в многочисленных блокировщиках...
(сколько кода написано! А ещё говорят что
template раздувают код!)
Но больше ругали компилятор, OS, Гей Тса и матерились.
Кажется, начался "переезд" в single-thread.
Жить-то как-то надо.
Может и до сих пор матерились бы...
И это
всего лишь какая-то "вшивая" sequence point!
Эпилог
welldoing)
Кстати, за исправление и советы по организации
блокировок тоже не заплатили.
"Нефиг за 30 строчек платить, у нас люди по 10000 пишут!"
Интересно, что и когда у них там опять "стрельнёт"?
А общественности дарю идеи (без реализации).
Т.к. за них не заплатили они не принадлежит конторе,
а критику услышать хочется.
template < class T, class ctritical_section = Ваша_любимая >
//Ресурс разделяемый между потоками.
class shared_resource
{
typedef shared_resource<T,ctritical_section> self;
friend class lock<T,ctritical_section>;
//Может предоставлять дополнительный сервис. см. ниже
ctritical_section cs;
//Важно при истинной паралельности. Процессоры не будут "драться".
//Возможно лучше спрятать это внутри ctritical_section и обеспечить
//STATIC_ASSERT(sizeof(ctritical_section)%processor_cache_line==0);
//и размещение ctritical_section по адресам кратным processor_cache_line
//ctritical_section и data не должны попадать в одну кэш-линию.
char cache_align[processor_cache_line-1];
//Без блокировки доступ к data крайне затруднён. Забыть lock невозможно.
//Можно реализовать в виде private наследования.(учитывайте cache_align)
//Возможно лучше использовать указатель ctritical_section* cs;
T data;
//А кому не лень может даже разложить всё это на стратегии.
public:
//По умолчанию сгенериться не совсем то что надо.
//(И зачем Страуструп эти умолчания сделал?)
self& operator=(self const&);
explicit//template конструктор НЕ является конструктором копий.
shared_resource(self const&);
//Да! lock это smart-pointer
lock<T,ctritical_section> operator&();
shared_resource();
//Передадим аргументы конструктору T
template<class A>
explicit
shared_resource(A const&);
template<class A,class B>
shared_resource(A const&,B const&);
template<class A,class B,class C>
shared_resource(A const&,B const&,C const&);
...
//Можно и кучку operator-ов добавить.
operator T()const;//а НЕ operator T const&()const;
void swap(self&);//Может уменьшать вероятность dead-lock см. ниже
self& operator=(T const&);
//и т.д.
...
};
//Если конструктор ресурса T использует НЕ const ссылку используйте
template<class T>
class out_argument
{
T& tmp;
public:
operator T&() const throw()
{ return tmp; }
explicit out_argument(T&src) throw()
: tmp(src) {}
};
//Например если использовать конструктор you_class::you_class(std::istream&);
//то можно написать так:
shared_resource<you_class> object_name(out_argument(std::cin));
//А доступаться с блокировкой так:
(&object_name)->do_something();
access(object_name)->do_something();
template < class T, class ctritical_section = Ваша_любимая >
//Блокирует и предоставляет доступ к ресурсу.
class lock{
shared_resource<T,ctritical_section>* ptr;
//Не надо его по new ! Ещё delete забудете!
void* operator new(size_t);
//Попрячем это пока. Ежели понадобиться сделаем public
//Опять же не будет лёгкой возможности lock в другой поток передать.
//Вдруг я там просто счётчик инкрементирую. Ресурс то уже залочен.
lock(lock const&);
lock& operator=(lock const&);//Аналогично.
public:
~lock();//Забыть unlock невозможно. И throw не страшны.
lock(shared_resource<T,ctritical_section>&);
//Не вызывайте явно: T* ptr = access(X).operator->();
//Не запоминайте указатель T*
//Внутри T::any_method() не запоминайте this;
T* operator->()const throw();
T& operator* ()const throw();//Не запоминайте ссылку!
//можно и так:
lock& operator=(class non_complete*);//Снимем блокировку так: access_ptr=0;
//далее кто как хочет и умеет:
...
};
//Ну и это для удобства. Не писать же везде lock<my_type>(my_object)
template<class T>
typename lock<T>
access(shared_resource<T>&src)
{
return lock<T>(src);
}
Если в критической секции ведётся протоколирование lock\unlock
то можно детектировать ПОТЕНЦИАЛЬНЫЕ dead-lock
когда различен порядок lock (без unlock) в РАЗНЫХ потоках.
поток X: ... lock(A) ... lock(B) <dead-lock> ... unlock(B) ... unlock(A) ...
поток Y: ... lock(B) ... lock(A) <dead-lock> ... unlock(A) ... unlock(B) ...
Это, конечно, не гарантирует их полного отсутствия
но практически очень удобно, выявляет тонкие логические
несуразности и позволяет иерархически группировать
и ассоциировать ресурсы с critical_section.
В этом смысле swap можно реализовать примерно так
void swap(T&a,T&b){
//Важен одинаковый порядок блокировок.
if(compare_critical_section_id(a,b){//ID могут быть просто адресами.
std::swap(a,b);
}else{
std::swap(b,a);
}
}
УФ! Упарился! Хватит.
Надеюсь, что эти идеи помогут тем кто не гониться за объёмами кода,
а пьеса объяснит некоторым работодателям как оно в жизни бывает.