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

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

почему свой тип для этого не написать. например что-то типа такого:
Код: 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
30.07.2014, 19:34
    #38709448
gandjustas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
lock на два случайных объекта.
РостигайЕвгенийВ,

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

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

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

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

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

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

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

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

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

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

"Вы написали аналог боксинга - оборачивание value Тип в reference тип :)" - да я знаю, что я написал. просто, имхо, это удобнее, чем object
...
Рейтинг: 0 / 0
30.07.2014, 20:32
    #38709491
mikron
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
lock на два случайных объекта.
Lelouch,
Почему бы не так?
Код: c#
1.
2.
3.
4.
Class A{
Static int seed;
Object mylock = interlocked.increment(ref seed);
}
...
Рейтинг: 0 / 0
30.07.2014, 20:33
    #38709493
cdtyjv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
lock на два случайных объекта.
Для подобных ситуаций есть два типа решений:
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
30.07.2014, 21:21
    #38709517
Ростигай
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
lock на два случайных объекта.
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
30.07.2014, 21:35
    #38709523
cdtyjv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
lock на два случайных объекта.
Ростигай ,
1) Если у вас в команде абы кто бездумно делает абы что, то это проблема команды. Монитор на самом объекте - нормальное рабочее решение, если вы его берете на объектах своих классов. Вам кирпич может на голову ВНЕЗАПНО упасть. Вы же не ходите из-за этого в каске?
2) В приведенном вами примере дедлока не может быть в принципе, так как для его возникновения нужно как минимум два монитора, а у вас он один.
...
Рейтинг: 0 / 0
30.07.2014, 23:28
    #38709573
gandjustas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
lock на два случайных объекта.
Ростигай,

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

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

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


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