powered by simpleCommunicator - 2.0.55     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / lock на два случайных объекта.
18 сообщений из 18, страница 1 из 1
lock на два случайных объекта.
    #38709357
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Привет.

Необходимо в методе вести работу с двумя объектами одного класса. Метод может вызываться параллельно.

При этом, необходимо обеспечить блокировку lock на время работы с обоими объектами.
Т.к. работа идет параллельно, то вместо одной глобальной блокировке решил использовать уникальную блокировку для каждого объекта класса. У каждого экземпляра класса, в конструкторе создается поле по которому будет идти блокировка lock (имя LockObject, но вот тип мне не ясен как раз) - по нему и будем локать.

Дедлок возникнет, если вызвать один метод передав одни и те же параметры, просто в обратном порядке. Поэтому, чтобы не было дедлока, нужно локать эти объекты в определенном порядке.Я решил просто выяснять старшинство этих LockObject и локать сперва меньший (само собой я сначала проверяю, что не передан один и тот же объект)

Но какой объект использовать для LockObject ?
1. Он должен обладать возможностью сравнения (CompareTo)
2. Он не должен быть структурой
3. Он должен быть уникальным при генерации.
4. Не должен когда-то переполнится (т.е. int не подойдет, т.к. в теории, если программа проработает неделю, то исчерпает размерность int. В принципе можно "зациклить", начав с 0 - но что-то сложно выходит)

Изначально я планировал использовать GUID.
Но это структура. Насколько я понимаю lock на структуру ОШИБОЧЕН, т.к. будет боксинг и локинг будет на созданный объект. Если другой поток тоже локнет эту структуру, то в итоге он залочит совершенно другой объект !

Другой вариант - использовать простой Object. Но он не поддерживает CompareTo. Т.е. нельзя вычислить старшинство, чтобы избежать дедлока.

Подскажите как быть.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709396
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Как вариант... Использовать таки GUID, но забоксить сразу при создании:

Код: c#
1.
private object guid = Guid.NewGuid()



Или могут быть какие-то проблемы ? Есть другие предложения ?
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709397
petalvik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ростигайнужно локать эти объекты в определенном порядке.
Можно посмотреть эту статью - Advanced Techniques To Avoid And Detect Deadlocks In .NET Apps . Там описана техника под названием Lock Leveling. Предназначена как раз для недопущения вызова блокировок в неправильном порядке.

Структуру (значимый тип) действительно использовать нельзя по причине боксинга.

Вместо object в принципе можно использовать любой произвольный ссылочный тип с любыми методами, в том числе comparable.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709418
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ростигай,
А что за работа с двумя объектами?
Можно подробней про решаемую задачу?
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709420
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
petalvik,

Да. В том и дело, что можно использовать любой Comparable. Нужно лишь, чтобы в конструкторе этому объекту задавалось нечто уникальное. Мне в голову приходит только GUID.

Спасибо за ссылку, почитаю, может чего найдут там.
В принципе, если идей не появится, думаю использовать свою - сразу хранить ссылку на object а не на guid.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709435
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ЕвгенийВ,

Да задача то простая. Синхронно обновить данные в обоих объектах.

Нужно предотвратить классическую проблему многопоточности не атомарных операций "считали-изменили-перезаписали измененное значение". Проблема лишь усложнилась из-за того, что принимают участие два объекта.

Глупо использовать одну глобальную блокировку на все объекты данного класса - это уж совсем узкое горлышко с одной полосой выходит. Решил использовать персональные блокировки у каждого объекта, нужно лишь задать порядок блокировок "по старшинству", чтобы исключить случайный дедлок. Остается лишь обеспечить, что персональные блокировки будут уникальные - GUID это обеспечивает.
В принципе данный алгоритм можно расширить на сколь угодное количество объектов - нужно лишь обеспечить строгий порядок блокировок.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709441
Lelouch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ростигай,

почему свой тип для этого не написать. например что-то типа такого:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
    public class LockTarget : IComparable
    {
        private readonly Guid _identifier;

        public LockTarget() : this(Guid.NewGuid()) { }

        public LockTarget(LockTarget src) : this(src._identifier) { }

        public LockTarget(Guid identifier)
        {
            _identifier = identifier;
        }

        public int CompareTo(object obj)
        {
            var target = obj as LockTarget;
            if (target == null)
                return 1;
            return _identifier.CompareTo(target._identifier);
        }
    }
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709448
gandjustas
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
РостигайЕвгенийВ,

Да задача то простая. Синхронно обновить данные в обоих объектах.

Нужно предотвратить классическую проблему многопоточности не атомарных операций "считали-изменили-перезаписали измененное значение". Проблема лишь усложнилась из-за того, что принимают участие два объекта.

Глупо использовать одну глобальную блокировку на все объекты данного класса - это уж совсем узкое горлышко с одной полосой выходит. Решил использовать персональные блокировки у каждого объекта, нужно лишь задать порядок блокировок "по старшинству", чтобы исключить случайный дедлок. Остается лишь обеспечить, что персональные блокировки будут уникальные - GUID это обеспечивает.
В принципе данный алгоритм можно расширить на сколь угодное количество объектов - нужно лишь обеспечить строгий порядок блокировок.

А просто сравнивать GetHashCode() объектов, а потом лочить сначала меньший, зачем больший?
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709475
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
gandjustas,

По сути вы предлагаете использовать GetHashCode вместо CompareTo - а все остальное оставить как у меня ?

Зачем сравнивать Хэши на старшинство, если можно сравнить старшинство сразу Гуид (IComparable<Guid>) ?
Если сравнение гуидов оптимизировано (проверить старший байт, и сразу выяснить кто тут старший) то это наверняка будет быстрее чем вычислять и сравнивать Хэш на равенство. Хэши не уникальны. У разных объектов могут выпасть идентичные, и как тогда выяснять кого лочить первым ?
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709482
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Lelouch,

Вы написали аналог боксинга - оборачивание value Тип в reference тип :)

Я тоже самое и хочу использовать, только без доп классов.

Код: c#
1.
2.
3.
4.
5.
6.
private object guid = Guid.NewGuid(); // Сразу создаем object по которому и будем лочить
...

lock (guid)
{
}
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709485
Lelouch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ростигай,

"Вы написали аналог боксинга - оборачивание value Тип в reference тип :)" - да я знаю, что я написал. просто, имхо, это удобнее, чем object
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709491
mikron
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lelouch,
Почему бы не так?
Код: c#
1.
2.
3.
4.
Class A{
Static int seed;
Object mylock = interlocked.increment(ref seed);
}
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709493
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для подобных ситуаций есть два типа решений:
1) Отдельный монитор на каждый объект. Это не обязательно какое-то поле объекта. Вы можете использовать монитор самого объекта. Как сравнивать - решать вам. Если это поле, что лично я считаю плохой идеей, то либо Guid, либо монотонно возрастающий/убывающий интеджер. Если сам объект - то смотрите сами по ситуации, главное что бы всегда можно было задать однозначное соответствие двух объектов. Hash code здесь, разумеется не подходит никаким боком, так как он не уникален.
2) Striped lock. Выделяете, например, 64 объекта, и кладете их в массив:
Код: c#
1.
2.
3.
4.
object[] locks = new object[64];

for (int i = 0; i < locks.Length; i++)
    locks[i] = new object();


Далее, когда вам надо залочить два объекта, делаете примерно следующее:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
int idx1 = object1.GetHashCode() % 64;
int idx2 = object2.GetHashCode() % 64;

if (idx1 < idx2) {
    int tmp = idx1;
    idx2 = idx1;
    idx1 = tmp;
}

lock (locks[idx1]) {
    lock (locks[idx2]) {
        ...
    }
}
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709517
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
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.
class OtherClass
{
  static void ExternalMethod (MyClass obj)
  {
      lock (obj) 
      {
       obj.MyMethod ();
      }
  }
}

class MyClass
{
private MyMethod ()
{
   lock (this)
   {
/*
  Тут не будет деадлока, если идет вызов в том же потоке (lock - реентерабельна в пределах потока).
  Однако, если метод ExternalMethod  вызывалась из другого потока, то будет дедлок
*/
   }
}
}



Тут пример маленький, и все видно. В реальной ситуации у вас может выйти цепочка вызовов, так, что сразу и не заметишь, такую взаимоблокировку.
Вероятность конечно довольно мала. Но если уж такое случится, вы будете гадать откуда дедлоки или тормоза. Безопасней изначально писать алгоритм, с использованием приватных объектов для блокировки.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709523
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ростигай ,
1) Если у вас в команде абы кто бездумно делает абы что, то это проблема команды. Монитор на самом объекте - нормальное рабочее решение, если вы его берете на объектах своих классов. Вам кирпич может на голову ВНЕЗАПНО упасть. Вы же не ходите из-за этого в каске?
2) В приведенном вами примере дедлока не может быть в принципе, так как для его возникновения нужно как минимум два монитора, а у вас он один.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709573
gandjustas
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ростигай,

У вас будут столь долгоживущие объекты, что int переполнится? Даже если по 1000 в секунду генерить, то хватит на 20 дней.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709578
gandjustas
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ростигай,

В примере кода проблем нет и не будет. Вы лочите один объект, а не несколько. Для дедлока нужно как минимум два. Рекомендуется лочить SyncRoot по причине быстродействия.
...
Рейтинг: 0 / 0
lock на два случайных объекта.
    #38709962
Ростигай
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
gandjustas,

Ну. Win-служба стоит на сервере, который работает, вроде бы как, годами... Я просто не хотел бы рисковать. Пусть себе будет Guid.
...
Рейтинг: 0 / 0
18 сообщений из 18, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / lock на два случайных объекта.
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]