volatile (программистское)
Jan. 22nd, 2007 03:23 amНаткнулся на старую статью Александреску о том, как можно использовать ключевое слово volatile с объектами (а не примитивными значениями, как обычно) в C++, для того, чтобы лучше многопоточные программы писать.
Интересная техника! Но любопытно то, что Александреску нигде не отмечает, насколько предлагаемое им использование слова volatile не зависит, на самом деле, от его "смысла". Пусть меня поправят, если я неправ, но мне кажется, что предлагаемое автором использование volatile только при определении объектов, а также полей и методов классов, которые могут без опаски быть использованы одновременно разными потоками - тут volatile вообще ничего не делает с точки зрения компилируемого кода (volatile может что-то делать при использовании с примитивными типами, но как раз этого Александреску советует не делать, а заворачивать их в таких случаях в объекты!).
Иными словами, то, что нужно, это некое абстрактное ключевое слово foobaric, так чтобы: а) любой объект можно было объявить foobaric; б) в таком случае его члены автоматически становятся foobaric; в) метод класса можно было объявить foobaric; г) foobaric-объект может вызывать только foobaric-методы своего класса; д) от объекта можно отобрать его foobaric-сущность каким-нибудь приведением.
Если у нас есть такой foobaric, то мы можем сделать умный указатель а-ля Александреску, который все время своей жизни проводит внутри мьютекса, и переводит вызовы foobaric-объекта, которым был инициализирован, в вызовы того же объекта без foobaric, который он сохранил внутри путем приведения.
Александреску делает это при помощи foobaric==volatile, но это не использует ничего из "настоящей" семантики volatile, а просто применяет volatile в качестве такого foobaric-маркера. Можно было бы, например, в качестве foobaric использовать const и mutable (для методов), но тогда "настоящая" семантика const мешалась бы под ногами. Сам факт того, что для решения этой проблемы пришлось "позаимствовать" вообще-то предназначенное для другого (хоть на первый взгляд и родственного) слово volatile, указывает на то, что, возможно, стоит иметь гибкую поддержку такого рода "маркеров" прямо в языке. И тут, видимо, мне самое время прочитать статьи про Traits, которые недавно бросил в закладки, но пока не добрался...
no subject
Date: 2007-01-22 01:53 am (UTC)1. В большинстве более-менее серьезных real-time приложениях не два класса операций, а больше. Например, ну что бы такого широко известного .. ну, скажем, linux kernel. Есть классы 1) может спать 2) может сидеть в спинлоке 3) медленная операция (нельзя использовать в interrupt handler) и т.п.
2. Механическое принудительное использование операций с локами для доступа к shared objects (a-la java) неэффективно. Программисту даден мозг на то, чтобы смотреть есть race condition или нет
no subject
Date: 2007-01-22 06:33 am (UTC)2. У программиста мозг стоит денег за каждый отработанный час. А компилятор работает практически забесплатно. Поэтому переложить хотя бы часть работы с программиста на компилятор - дело хорошее и правильное (хотя и не всегда получается, да).
Другое дело, что на одних traits избавиться от race condition все равно не получится...
Какой текст!
Date: 2007-01-22 01:57 am (UTC)Re: Какой текст!
Date: 2007-01-22 09:49 am (UTC)no subject
Date: 2007-01-22 03:17 am (UTC)no subject
Date: 2007-01-22 09:49 am (UTC)no subject
Date: 2007-01-22 10:45 am (UTC)no subject
Date: 2007-01-22 11:13 am (UTC)no subject
Date: 2007-01-22 04:16 am (UTC)no subject
Date: 2007-01-22 07:52 am (UTC)P.S. Лет 5-7 тому назад попытки использовать C++ в Embedded systems встречали жестокое сопротивление. Сейчас C++ используют в Embedded systems, но такое же жестокое сопротивление встречают попытки использования STL.
no subject
Date: 2007-01-22 08:00 am (UTC)P.S. Это несколько из другой оперы, как мне кажется
no subject
Date: 2007-01-22 09:49 am (UTC)no subject
Date: 2007-01-22 04:21 am (UTC)no subject
Date: 2007-01-22 04:37 am (UTC)no subject
Date: 2007-01-22 08:13 am (UTC)no subject
Date: 2007-01-22 07:46 pm (UTC)Евгений Д.
no subject
Date: 2007-01-22 09:50 am (UTC)no subject
Date: 2007-01-22 09:13 am (UTC)что ООП в форме С++- это ужас...
А уж серьезный мультитрединг в С++ - это ужас в квадрате
:))
no subject
Date: 2007-01-22 09:50 am (UTC)no subject
Date: 2007-01-22 10:03 am (UTC)Но С++... кстати Objective C судя по всему тоже очень удобен.. надо бы к нему серьезней отнестись....
А вообще мне кажется что серьезный мультитрединг надо писать на POSIX threads без всяких там абстракций, энкапсуляций и т.п. Потому что на гоночную машину не ставят автоматическую коробку передач.
no subject
Date: 2007-01-22 06:49 pm (UTC)Популярное заблуждение. Ручная передача может стоять на дорожном спортивном автомобиле, водитель которой хочет почувствовать себя крутым гонщиком. А на Формулах-1 ставят автоматические коробки, которые гораздо эффективнее самого лучшего гонщика с ручной коробкой. Единственная причина, по которой в некоторых гоночных машинах не используют автомат - это то, что это запрещено правилами, как unfair advantage.
Похоже, то же самое и с программированием. Cool 3133T hackers могут писать на ассемблере. Однако компании, которые рулят, не чураются прогресса.
Но вот в чем я соглашусь. Хорошему программисту нужно знать, что происходит под уровнем абстракции, с которым он работает, хотя бы на 1 уровень вниз. То есть настоящий шумахер обязан уметь водить как на ручной коробке, так и на автомате :)
no subject
Date: 2007-01-27 12:49 pm (UTC)Вот если бы вы сказали про антипробуксовочную систему или launch control, то с вами осталось бы только молча согласится.
no subject
Date: 2007-01-26 04:11 am (UTC)Опыт у меня с ним самый негативный.
Один reference-counted gargage collection сколько нервов испортит. А про эппловский IDE (который Xcode) уже и не говорю.
no subject
Date: 2007-01-22 09:54 am (UTC)no subject
Date: 2007-01-22 09:59 am (UTC)no subject
Date: 2007-01-22 11:21 am (UTC)Но пинают почему-то как раз С++.
И спасибо за рекомендацию.
no subject
Date: 2007-01-22 05:13 pm (UTC)no subject
Date: 2007-01-22 10:09 am (UTC)no subject
Date: 2007-01-22 10:30 am (UTC)В чем смысл утверждения о том, что мультитрединг под pthreads лучше чем мультитрединг под С++?
Кто мешает использовать pthreads под С++? Или же С++ навязывает иные способы программирования мультитреда?
no subject
Date: 2007-01-22 10:31 am (UTC)no subject
Date: 2007-01-22 10:44 am (UTC)no subject
Date: 2007-01-22 10:44 am (UTC)сам факт необходимости написания такой статьи на которую ссылается Авва это доказывает.
В отличии от скажем Java/C# где написание нультитрединговой аппликации а также читабельность подобного кода ( в попытке понять что он делает и как работают намного облегчены).
А POSIX Threads/ C code - просто наиболее удобное сочетание для написания сложных мульитрединговых програм с элементами real-time
no subject
Date: 2007-01-22 11:15 am (UTC)По мелочам:
> Проблема с С++ - это сложность в понимании/написании/употреблении мультитрединговой модели с точки зрения организации правильного доступа к данным/синхронизации и т.п.
Чего я не могу понять - это почему POSIX Threads/C code не страдает теми же проблемами, а наоборот есть "наиболее удобное сочетание для написания сложных мульитрединговых програм с элементами real-time".
> сам факт необходимости написания такой статьи на которую ссылается Авва это доказывает
В posix-threads на С без "плюсов" проблемы те же самые, просто там даже не будет шанса их решить, как попытался на С++ Александруску. Но от этого необходимость никуда не денется.
no subject
Date: 2007-01-22 02:08 pm (UTC)> будет шанса их решить, как попытался на С++ Александруску. Но от этого
> необходимость никуда не денется.
Плюсы добавляют проблем, т.к. больше шансов получить race condition в неожиданном месте из-за такой штуки, как время жизни объекта. Вот, например, древний thread (http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/e9447e4f8df38f68/158bb2b629d70dfb) на c.l.c++.m с говорящим названием "Destruction, Derivation, Synchronization, & Chaos", начатый бустоводом Gerhard Menzl.
Что характерно, boost::signals - библиотека похожего назначения с описанной в треде по ссылке - не поддерживает многопоточность (во всяком случае, не поддерживала, когда я на нее последний раз смотрел).
Другой пример - невозможность безопасно и переносимо реализовать синглтон Майерса, т.к. после вызова конструктора статического объекта нужно взвести некоторый скрытый флажок, и, значит, доступ к этому флажку нужно тоже синхронизировать, а возможности такой нет.
Можно еще взглянуть, насколько усложнена реализация boost::shared_ptr для корректной работы в многопоточной среде.
Т.е., резюмируя, попытки использовать так называемый "modern c++ design" в условиях многопоточности значительно повышают шансы нарваться на грабли.
Так что я присоединяю свое ИМХО к сообщениям ораторов, высказавшихся против наворотов в духе Александреску в многопоточных программах.
no subject
Date: 2007-01-22 03:52 pm (UTC)no subject
Date: 2007-01-22 04:27 pm (UTC)Но за "навороты в духе Александреску в многопоточных программах" я никак не агитирую. У меня та статья с обещаниями дать средство устранения race conditions в своё время вызвала лишь недоумённую улыбку.
Примеры Ваши действительно интересные.
Что до времени жизни объекта, то обычный С программист ведь тоже может "догадаться" начать уничтожать структуру данных, которая где-то еще зарегистрирована. Автор примера хотел получить некоторый автоматизм деригистрации из деструктора базового класса, который (автоматизм) кривыми средствами С++ красиво написать вроде бы не получается. Но в обычном С дерегистрацию тоже придется как-то вызывать, чтобы не получить того же самого race condition, и кто сказал, что это будет удобнее, чем делать это на С++? Ну да, хорошо бы всю дерегистрацию запрятать в деструктор базового класса, но можно ведь и не прятать, как и в С (без плюсов) никаких деструкторов не имеющем.
С++, конечно, не оправдывает некоторых естественных ожиданий, но желание дерегистрировать частично разрушенный класс - оно вовсе не естественно.
no subject
Date: 2007-01-27 07:16 am (UTC)В этой реализации не увидел ничего для мультитреда, что было бы специфично связано с С++, но отсутствовало бы при попытке сделать что-то подобное на С. Этого нет ни в заложенных возможностях shared/weak_ptr, ни в его имплементации.
Иными словами, я понимаю, что код непростой, не вижу только как он станет проще под чистым С, а также не вижу, почему кто-то захочет отказаться от каких-то возможностей shared_ptr под С, желая их всё же иметь под С++.
no subject
Date: 2007-01-23 01:12 am (UTC)нультитрединговая аппликация ака беспоточное приложение :-)
no subject
Date: 2007-01-22 02:37 pm (UTC)no subject
Date: 2007-01-22 03:43 pm (UTC)no subject
Date: 2007-01-22 06:52 pm (UTC)no subject
Date: 2007-01-22 07:46 pm (UTC)no subject
Date: 2007-01-22 07:41 pm (UTC)(A while ago, after having published "volatile: Multithreaded Programmer's best friend", the MT programmer community ripped my right leg and beat me to death with it. That was because my article had an introduction of the 'volatile' keyword that contained a number of gross inaccuracies, the main one being that volatile helps MT programming. For the record, the meat of the article still stands, but it's a shame it was blurred by some bads in the introduction., http://groups.google.com/group/comp.std.c++/msg/9ab9f0357570edb8)
Впрочем, volatile можно было бы заменить каким-то модификатором вроде safe_for_multithreaded_access. Однако непохоже, чтобы это решало хоть какие-нибудь значимые проблемы при написании многопоточного кода.
Насколько я понял идею, она состоит в том, чтобы с помощью системы типов обязать клиентский код "оборачивать" доступ к защищённым методам (к тем, у которых модификатор safe_for... отсутствует) мьютексом. Поскольку проконтролировать, какой именно мьютекс используется, так нельзя, этот метод даёт даже меньше чем просто разумная инкапсуляция доступа. Иными словами, если "незащищённый" метод доступен снаружи, необходимость захватить хоть какой-то мьютекс -- очень слабое условие.
Впрочем, возможность на уровне системы типов отличать данные, к которым доступ имеют много потоков исполнения от данных (общие), к которым имеет доступ только один поток (приватные) могла бы быть полезной для статического поиска ошибок синхронизации. Такие вещи пытались делать, в частности для Java.
Ещё одно замечание по поводу traits. Не факт, что свойства класса или объекта способны описать корректность многопоточного кода. Во всяком случае, довольно бесполезно маркировать thread-safety (бессмысленное словосочетание!) объектов нижнего уровня.
Более интересный, как мне кажется, подход -- возможность делать любые комбинации изменений атомарно, сохраняя тем самым сколь угодно сложные инварианты. См., например, Harris, Marlow, Peyton Jones, Herlihy, Composable Memory Transactions (http://research.microsoft.com/Users/simonpj/papers/stm/stm.pdf).
Евгений Д.
no subject
Date: 2007-01-22 08:05 pm (UTC)no subject
Date: 2007-01-22 08:39 pm (UTC)Евгений Д.
no subject
Date: 2007-01-23 01:54 am (UTC)вот, предположим, придумали мы ключевое слово ThreadSafe, и радостно припендюрили его к нашим мультитредно-защищённым классам. а один класс (или метод) забыли. как мы найдём, что вот к этому классу вдруг в рантайме стали лезть из нескольких тредов одновременно? проблема-то ведь не в том, как замаркировать код как thread safe, а как предотвратить/идентифицировать мультитредный доступ к коду, который не thread safe.
no subject
Date: 2007-01-23 02:17 am (UTC)Опасность именно обратная: что мы какой-то класс неправильно пометили как ThreadSafe. Тогда упс.
no subject
Date: 2007-01-23 09:28 am (UTC)вот это, как это будет достигаться, что не скомпилируется?
вот есть что-то не thread safe, с чем мы работаем однотредно. никаких "ритуальных танцев" пока мы требовать не можем, ибо должны подчиняться принципу "платим только за то, чем пользуемся". и вдруг в какой-то момент мы бах! и доступаемся к этому из другого треда. в рантайме это, предположим, отловить можно, а при компиляции-то как?
no subject
Date: 2007-01-23 09:46 am (UTC)С volatile это работает потому, что компилятор не даст вам вызвать non-volatile method у volatile объекта. С каким-то другим гипотетическим ключевым словом это работало бы схожим образом - компилятор бы знал о нем, и не давал бы кому-то, кто им помечен, вызывать кого-то, кто не помечен, без ритуального танца.
вот есть что-то не thread safe, с чем мы работаем однотредно. никаких "ритуальных танцев" пока мы требовать не можем, ибо должны подчиняться принципу "платим только за то, чем пользуемся". и вдруг в какой-то момент мы бах! и доступаемся к этому из другого треда. в рантайме это, предположим, отловить можно, а при компиляции-то как?
От нас тут требуется, чтобы мы и при однотредной работе в исходнике выполняли виртуальный танец, когда имеем дело с non-thread-safe объектом. Конечно, это нарушает принцип "платим только за то, чем пользуемся", и в конечном итоге для того, чтобы соблюдать этот принцип, нам приходится заранее решать, какой код никогда никогда не будет мультипоточным, и просто не пользоваться для ним этим методом; а если все же окажется, что его вызовут, то баг и капут.