http://msdn.microsoft.com/en-us/magazine/cc163744.aspx
For a lock to provide mutual exclusion for a region of memory, no writes to that memory can occur without entering the same lock. In a properly designed program, associated with every lock is a region of memory for which it provides mutual exclusion. Unfortunately, there is no obvious artifact of the code that makes this association clear, and yet this information is absolutely critical for anyone reasoning about the multithreaded behavior of the program.
what would be the ideal syntax? what comes to mind is
For a lock to provide mutual exclusion for a region of memory, no writes to that memory can occur without entering the same lock. In a properly designed program, associated with every lock is a region of memory for which it provides mutual exclusion. Unfortunately, there is no obvious artifact of the code that makes this association clear, and yet this information is absolutely critical for anyone reasoning about the multithreaded behavior of the program.
what would be the ideal syntax? what comes to mind is
object myLock = new object(); [Protected(myLock)] MyState myState;x-posted
(no subject)
Date: 2008-07-24 05:28 am (UTC)(no subject)
Date: 2008-07-24 08:42 am (UTC)(no subject)
Date: 2008-07-24 12:26 pm (UTC)Во-первых, зачем отдельный myLock, если точно так же можно лочить само myState (и подразумевается, что оно действительно лочится при каждом доступе)?
Во-вторых, это вовсе не решает проблемы. Доступ к полям myState и так практически всегда атомарный, там лок и не нужен. Лок нужен для обеспечения более длительных операций, типа "считать из myState состояние, что-то сделать, записать в myState новое состояние", а никакой компилятор не сможет автоматически угадать, что ты имеешь в виду, какую именно подпоследовательность операций ты хочешь сделать атомарной. Более того, он не должен заниматься таким угадыванием, потому что одна ошибка - и пиздец, причём пиздец вовсе не мгновенный и ясно видимый, а наоборот неслышно крадущийся в зарослях и выжидающий.
(no subject)
Date: 2008-07-24 12:30 pm (UTC)Но, с другой стороны, это не намного короче/понятней, чем тупой lock(myState) { bla bla bla } внутри (в который такой атрибут и будет, собственно, раскрываться).
(no subject)
Date: 2008-07-24 02:20 pm (UTC)Это ерунда какая-то. Лок используется чтобы сериализовать исполнение неатомарной операции. Writes совершенно непричем, write может быть атомарным и не требовать защиты, read может требовать защиты без всяких write
(no subject)
Date: 2008-07-24 03:28 pm (UTC)(no subject)
Date: 2008-07-24 08:21 pm (UTC)защищаются всегда ресурсы, не куски кода. кусок кода нужно защищать лишь постольку, поскольку он работает с ресурсом и только на время, пока нарушается инвариант. там в статье всё объясняется простым языком, для dummies :)
(no subject)
Date: 2008-07-24 08:29 pm (UTC)во-вторых, предложенный синтаксис решает не проблему собственно локинга, а проблему "there is no obvious artifact of the code that makes this association clear". то есть, после добавления такого атрибута компилятор (если может), или рантайм (если компилятор не может) должны бросать ошибку типа "attempt to access shared resource myState when myLock is not acquired".
зачем отдельный lock, про это много литературы написано, в том числе и по ссылке. но если девелопер считает, что он знает лучше, он может написать [Protected(myState)] MyState myState; или там просто [Protected] myState, что то же самое, что, как ты написал, [Synchronized] myState, ну или так, как Антон написал, как в джаве делается.
(no subject)
Date: 2008-07-24 08:32 pm (UTC)(no subject)
Date: 2008-07-24 08:41 pm (UTC)thread 1:
name = person.getName();
height = person.getHeight();
weight = person.getWeight();
thread 2:
printf("Name: %s weight %d height %d\n", name, height, weight);
какой конкретно ресурс тут защитит лок, гарантирующий что мы печатаем имя и вес одного человека?
(no subject)
Date: 2008-07-24 08:43 pm (UTC)Правда, есть ряд сложностей. Во-первых, такая запись может вызвать Ложное Чувство Защищённости™. Во-вторых, далеко не всегда ресурс == один объект. А так -- да, в принципе довольно правильная штука.
(no subject)
Date: 2008-07-24 09:33 pm (UTC)(no subject)
Date: 2008-07-24 09:46 pm (UTC)(no subject)
Date: 2008-07-25 12:25 am (UTC)извени! :)
можешь привести пример, когда read без write требует защиты?
(no subject)
Date: 2008-07-25 12:30 am (UTC)name, height and weight, которым присваиваются значения, это свойства какого объекта? вот у этого объекта и нарушается инвариант, его и надо лочить. или подробнее надо?
(no subject)
Date: 2008-07-25 12:38 am (UTC)объявить класс synchronized - это всё равно что обернуть каждый метод, включая сеттеры и геттеры, в lock(this). для одних задач слишком много, для других слишком мало. в статье приводятся многочисленные примеры (hashtable buckets, например).
(no subject)
Date: 2008-07-25 12:43 am (UTC)object myLock = new object();
[Protected(myLock)] MyState myState;
[Protected(myLock)] MyState2 myState2;
Ложное Чувство Защищённости
да, примерно так же, как automatic memory management вызывает ложное чувство защищённости от memory leaks.
(no subject)
Date: 2008-07-25 01:21 am (UTC)Так чо, где тут ресурс-то?
(no subject)
Date: 2008-07-25 07:51 am (UTC)lock(myLock)
{
tmp = myState;
}
lock(myLock)
{
myState2 += tmp;
}
Тогда как на самом деле всё должно было происходить внутри одного и того же лока, например. Или не должно. Но компилятор больше не ругается. Вдобавок этой штукой, видимо, переменные, а не объекты защищаются, поэтому ссылка может утечь и компилятор ничего не скажет.
Но в целом да, идея вполне правильная. Её даже наверное можно реализовать сторонней тулзой (или на базе fxCop'a какого-нибудь).
(no subject)
Date: 2008-07-25 05:14 pm (UTC)(no subject)
Date: 2008-07-25 05:55 pm (UTC)А кодом я, видимо, что-то имел в виду, но сам не пойму уже. Неважно, в общем смысл понятен, наверное.
(no subject)
Date: 2008-07-28 01:52 am (UTC)(no subject)
Date: 2008-07-28 08:12 am (UTC)(no subject)
Date: 2008-07-28 08:55 am (UTC)(no subject)
Date: 2008-07-28 09:36 pm (UTC)на словах, вроде, всё гладко. надо посмотреть на implementation каакой-нибудь. что меня прикололо, когда я читал, это что вначале декларируется non-blocking, а потом не только "in case of conflict transaction will wait before retry" (т.е. blocking), а и просто-таки все упомянутые там implementations internally всё равно используют locks.
неочевидно, поможет ли это рядовому девелоперу, или наоборот. сейчас я не должен вникать в то, каков именно у меня инвариант, который не должен нарушаться, достаточно знать, какие переменные/объекты в нём участвуют, и закрыть их локом, что гарантирует защиту инварианта от внешних воздействий. в том смысле, что только я сам могу его нарушить.
при tm же, см. раздел "implementation issues", внутри атомного блока инвариант не гарантирован, хотя именно там-то он мне и важен.
(no subject)
Date: 2008-07-28 09:37 pm (UTC)(no subject)
Date: 2008-07-28 10:18 pm (UTC)(no subject)
Date: 2008-07-31 07:29 pm (UTC)в рамках же описанного тобой замечательного дизайна ближайший объект, накоторый мапится этот disjoint set, это объект №4. и да, если его лочить, concurrency будет хуёвая, и кто в этом виноват?
дальше тут можно очень много слов сказать о том, как сделать лучше, но это всё будет в основном про оо дизайн, а не про мультитредность. в хорошем оо дизайне редко возникают вопросы, куда смапить инвариант; само наличие инварианта является фактором, влияющим на дизайн.
безусловно, бывают исключения, и я даже готов про них поговорить, но только в оо рамках. и я бы таки порекомендовал сначала статью прочитать, ибо там на многие простые вопросы очень хорошо отвечено, а то будет как с соловьёвым.
(no subject)
Date: 2008-07-31 07:30 pm (UTC)(no subject)
Date: 2008-07-31 07:41 pm (UTC)смысл твоего кода неожиданно стал понятен :)
ну да, эта проблема существует и сейчас, никуда не девается, но при чём здесь? повторить, какую проблему решает предложенный мной синтаксис? :)