|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Привет. Необходимо в методе вести работу с двумя объектами одного класса. Метод может вызываться параллельно. При этом, необходимо обеспечить блокировку lock на время работы с обоими объектами. Т.к. работа идет параллельно, то вместо одной глобальной блокировке решил использовать уникальную блокировку для каждого объекта класса. У каждого экземпляра класса, в конструкторе создается поле по которому будет идти блокировка lock (имя LockObject, но вот тип мне не ясен как раз) - по нему и будем локать. Дедлок возникнет, если вызвать один метод передав одни и те же параметры, просто в обратном порядке. Поэтому, чтобы не было дедлока, нужно локать эти объекты в определенном порядке.Я решил просто выяснять старшинство этих LockObject и локать сперва меньший (само собой я сначала проверяю, что не передан один и тот же объект) Но какой объект использовать для LockObject ? 1. Он должен обладать возможностью сравнения (CompareTo) 2. Он не должен быть структурой 3. Он должен быть уникальным при генерации. 4. Не должен когда-то переполнится (т.е. int не подойдет, т.к. в теории, если программа проработает неделю, то исчерпает размерность int. В принципе можно "зациклить", начав с 0 - но что-то сложно выходит) Изначально я планировал использовать GUID. Но это структура. Насколько я понимаю lock на структуру ОШИБОЧЕН, т.к. будет боксинг и локинг будет на созданный объект. Если другой поток тоже локнет эту структуру, то в итоге он залочит совершенно другой объект ! Другой вариант - использовать простой Object. Но он не поддерживает CompareTo. Т.е. нельзя вычислить старшинство, чтобы избежать дедлока. Подскажите как быть. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 18:15 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Как вариант... Использовать таки GUID, но забоксить сразу при создании: Код: c# 1.
Или могут быть какие-то проблемы ? Есть другие предложения ? ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 18:35 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Ростигайнужно локать эти объекты в определенном порядке. Можно посмотреть эту статью - Advanced Techniques To Avoid And Detect Deadlocks In .NET Apps . Там описана техника под названием Lock Leveling. Предназначена как раз для недопущения вызова блокировок в неправильном порядке. Структуру (значимый тип) действительно использовать нельзя по причине боксинга. Вместо object в принципе можно использовать любой произвольный ссылочный тип с любыми методами, в том числе comparable. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 18:36 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Ростигай, А что за работа с двумя объектами? Можно подробней про решаемую задачу? ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 18:55 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
petalvik, Да. В том и дело, что можно использовать любой Comparable. Нужно лишь, чтобы в конструкторе этому объекту задавалось нечто уникальное. Мне в голову приходит только GUID. Спасибо за ссылку, почитаю, может чего найдут там. В принципе, если идей не появится, думаю использовать свою - сразу хранить ссылку на object а не на guid. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 18:56 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
ЕвгенийВ, Да задача то простая. Синхронно обновить данные в обоих объектах. Нужно предотвратить классическую проблему многопоточности не атомарных операций "считали-изменили-перезаписали измененное значение". Проблема лишь усложнилась из-за того, что принимают участие два объекта. Глупо использовать одну глобальную блокировку на все объекты данного класса - это уж совсем узкое горлышко с одной полосой выходит. Решил использовать персональные блокировки у каждого объекта, нужно лишь задать порядок блокировок "по старшинству", чтобы исключить случайный дедлок. Остается лишь обеспечить, что персональные блокировки будут уникальные - GUID это обеспечивает. В принципе данный алгоритм можно расширить на сколь угодное количество объектов - нужно лишь обеспечить строгий порядок блокировок. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 19:14 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Ростигай, почему свой тип для этого не написать. например что-то типа такого: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 19:29 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
РостигайЕвгенийВ, Да задача то простая. Синхронно обновить данные в обоих объектах. Нужно предотвратить классическую проблему многопоточности не атомарных операций "считали-изменили-перезаписали измененное значение". Проблема лишь усложнилась из-за того, что принимают участие два объекта. Глупо использовать одну глобальную блокировку на все объекты данного класса - это уж совсем узкое горлышко с одной полосой выходит. Решил использовать персональные блокировки у каждого объекта, нужно лишь задать порядок блокировок "по старшинству", чтобы исключить случайный дедлок. Остается лишь обеспечить, что персональные блокировки будут уникальные - GUID это обеспечивает. В принципе данный алгоритм можно расширить на сколь угодное количество объектов - нужно лишь обеспечить строгий порядок блокировок. А просто сравнивать GetHashCode() объектов, а потом лочить сначала меньший, зачем больший? ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 19:34 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
gandjustas, По сути вы предлагаете использовать GetHashCode вместо CompareTo - а все остальное оставить как у меня ? Зачем сравнивать Хэши на старшинство, если можно сравнить старшинство сразу Гуид (IComparable<Guid>) ? Если сравнение гуидов оптимизировано (проверить старший байт, и сразу выяснить кто тут старший) то это наверняка будет быстрее чем вычислять и сравнивать Хэш на равенство. Хэши не уникальны. У разных объектов могут выпасть идентичные, и как тогда выяснять кого лочить первым ? ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 20:12 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Lelouch, Вы написали аналог боксинга - оборачивание value Тип в reference тип :) Я тоже самое и хочу использовать, только без доп классов. Код: c# 1. 2. 3. 4. 5. 6.
... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 20:17 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Ростигай, "Вы написали аналог боксинга - оборачивание value Тип в reference тип :)" - да я знаю, что я написал. просто, имхо, это удобнее, чем object ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 20:20 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Lelouch, Почему бы не так? Код: c# 1. 2. 3. 4.
... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 20:32 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Для подобных ситуаций есть два типа решений: 1) Отдельный монитор на каждый объект. Это не обязательно какое-то поле объекта. Вы можете использовать монитор самого объекта. Как сравнивать - решать вам. Если это поле, что лично я считаю плохой идеей, то либо Guid, либо монотонно возрастающий/убывающий интеджер. Если сам объект - то смотрите сами по ситуации, главное что бы всегда можно было задать однозначное соответствие двух объектов. Hash code здесь, разумеется не подходит никаким боком, так как он не уникален. 2) Striped lock. Выделяете, например, 64 объекта, и кладете их в массив: Код: c# 1. 2. 3. 4.
Далее, когда вам надо залочить два объекта, делаете примерно следующее: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.
... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 20:33 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
cdtyjv, Блокировка lock (this) - плохое решение. Нужно использовать именно внутренний объект. lock (this) доступна извне объекта. Если вы пишете многопоточный код, особенно работая в команде, коллега (или вы) в другом месте может сделать lock (ваш объект) и это внезапно совпадет с вашим lock (this) ! Т.е. два совершенно различных алгоритма попадут в одну блокировку. В лучшем случае вы получите снижение производительности - т.к. два совершенно разных процесса используют одно блокировку-горлышко для разных алгоритмов. В худшем, к тормозам получите еще и дедлок. Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.
Тут пример маленький, и все видно. В реальной ситуации у вас может выйти цепочка вызовов, так, что сразу и не заметишь, такую взаимоблокировку. Вероятность конечно довольно мала. Но если уж такое случится, вы будете гадать откуда дедлоки или тормоза. Безопасней изначально писать алгоритм, с использованием приватных объектов для блокировки. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 21:21 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Ростигай , 1) Если у вас в команде абы кто бездумно делает абы что, то это проблема команды. Монитор на самом объекте - нормальное рабочее решение, если вы его берете на объектах своих классов. Вам кирпич может на голову ВНЕЗАПНО упасть. Вы же не ходите из-за этого в каске? 2) В приведенном вами примере дедлока не может быть в принципе, так как для его возникновения нужно как минимум два монитора, а у вас он один. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 21:35 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Ростигай, У вас будут столь долгоживущие объекты, что int переполнится? Даже если по 1000 в секунду генерить, то хватит на 20 дней. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 23:28 |
|
lock на два случайных объекта.
|
|||
---|---|---|---|
#18+
Ростигай, В примере кода проблем нет и не будет. Вы лочите один объект, а не несколько. Для дедлока нужно как минимум два. Рекомендуется лочить SyncRoot по причине быстродействия. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.07.2014, 23:33 |
|
|
start [/forum/topic.php?fid=20&msg=38709357&tid=1402654]: |
0ms |
get settings: |
10ms |
get forum list: |
14ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
42ms |
get topic data: |
12ms |
get forum data: |
3ms |
get page messages: |
53ms |
get tp. blocked users: |
1ms |
others: | 16ms |
total: | 159ms |
0 / 0 |