powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Блокировка по ключу
14 сообщений из 39, страница 2 из 2
Блокировка по ключу
    #39359986
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
absinthe95 байт
у меня 68 получилось
Код: c#
1.
2.
3.
4.
5.
6.
			Int32 size = 65536;
			var used = GC.GetTotalMemory(false);
			var arr = new ReaderWriterLockSlim[size];
			Console.WriteLine((double)(GC.GetTotalMemory(false) - used) / size); // 4 массив указателей
			for(Int32 i = 0; i < size; i++) arr[i] = new ReaderWriterLockSlim();
			Console.WriteLine((double)(GC.GetTotalMemory(false) - used) / size); // 72 с массивом, без учета массива 68 (72-4)


При ограничении 65536 можно вместо ConcurrentDictionary использовать обычный массив. Займет 4,5 Мб (72*65536). Скорость выборки по индексу из массива намного быстрее.
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39359990
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
absintheвыглядит запутанно, тут я пытаюсь заткнуть любую щель
где неправильное значение проскочить
Щели не заткнуты: первый поток прошел проверку и прервался вторым, второй прошел проверку и добавил, затем первый заменил то что добавил второй. В итоге два потока используют разные ReaderWriterLockSlim.
Надо добавление оборачивать в блокировку.
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
private ConcurrentDictionary<string, WeakReference<ReaderWriterLockSlim>> _lockDic;

public ReaderWriterLockSlim GetLock(string key)
{
   WeakReference<ReaderWriterLockSlim> lockRef;
   ReaderWriterLockSlim lockObj;
   if(!_lockDic.TryGet(key, out lockRef) || (lockObj = lockRef.Target) == null) // Проверяем без блокировки
   {
      lock(_lockDic) {
       if(!_lockDic.TryGet(key, out lockRef) || (lockObj = lockRef.Target) == null) // повторяем проверку
      {
        добавляем
...


Ну и при удалении аналогичная конструкция. Проверка, блокировка, еще раз проверка, удаление.

Мне кажется что будет тормозить при больших нагрузках из-за постоянных new ReaderWriterLockSlim() и удаления их сборщиком мусора, плюс эта блокировка.
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39359993
absinthe
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima TНадо добавление оборачивать в блокировку.

зачем, если _lockDic.AddOrUpdate уже блокирует словарь
на изменение по ключу? пока происходит обновление по
ключу, никто не может по этому же ключу ничего сделать
даже TryGet в параллельном потоке будет ждать

два TryGet-а не нужно, потому что и первый нужен только
для скорости, чтобы не блокировало ресурс на чтение
из параллельных потоков, а второй точно не нужен,
потому что AddOrUpdate будет работать монопольно для ключа

почитал больше про WeakReference, пишут что ссылка может
моментально обнуляться, может надо устанавливать trackResurrection
в true, но тут я пока не понял, поможет ли это действительно
продлить жизнь ссылке
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39359994
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В принципе можно без доп. блокировки если в твоем коде заменить _lockDic.AddOrUpdate() на _lockDict.GetOrAdd()

Но при этом усложняется удаление из _lockDic, т.к. пока ты удаляешь другой поток может успеть создать ReaderWriterLockSlim.
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39359995
absinthe
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima TПри ограничении 65536 можно вместо ConcurrentDictionary использовать обычный массив. Займет 4,5 Мб (72*65536). Скорость выборки по индексу из массива намного быстрее.

если использовать обычный массив, придётся все операции
с ним обернуть в lock?
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39359996
absinthe
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima TНо при этом усложняется удаление из _lockDic, т.к. пока ты удаляешь другой поток может успеть создать ReaderWriterLockSlim

AddOrUpdate не даст это сделать дважды, если кто-то успел добавить
элемент, то произойдёт Update, если нет то Add

выглядит вроде потокобезопасно, неужели я не прав?
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39359997
absinthe
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima T,

попробую прокомментировать что я пытаюсь сделать

Код: 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.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
private ConcurrentDictionary<string, WeakReference<ReaderWriterLockSlim>> _lockDic;

public ReaderWriterLockSlim GetLock(string key)
{
   WeakReference<ReaderWriterLockSlim> lockRef;
   ReaderWriterLockSlim lockObj;
   // если
   // в словаре нет ссылки
   // или ссылка есть, но она уже обнулилась
   // (при чём сразу получим строгую ссылку)
   if(!_lockDic.TryGet(key, out lockRef) || (lockObj = lockRef.Target) == null)
   {
      // создаём новый объект блокировки и новую слабую ссылку
      var lockObj = new ReaderWriterLockSlim();
      var lockRef = new WeakReference<ReaderWriterLockSlim>(lockObj);
      // добавим или обновим ссылку
      _lockDic.AddOrUpdate(key, 
          // в словаре ещё нет значения, значит добавим ссылку
          (k) => lockRef, 
          // в словарь уже кто-то успел добавить ссылку
          // тогда произведём обновление 
          (k, v) => 
         {
             // получим строгую ссылку из имеющейся слабой ссылки
             var target = v.Target;
             // если ссылка живая
             if (target != null)
             {
                // то присвоим её локальной переменной, чтобы вернуть
                lockObj = target;
                // оставляем слабую ссылку без изменений
                return v;
             }
             // если ссылка уже не живая, то обновим на новую ссылку
             return lockRef;
         });
   }
   // к этому моменту мы гарантировано имеем живую строгую ссылку
   // существующую в единственном экземпляре
   // гарантируется, что других строгих ссылок на объект блокировки
   // по ключу ни в одном потоке нет
   return lockObj;
}



что я делаю не так?
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39359998
absinthe
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima T,

сегодня постараюсь протестировать все варианты
и самые первоначальные которые ты предложил,
мне понравился трюк с перекидыванием блокировки

как закончу напишу результаты и код
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39360000
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
absintheDima TНадо добавление оборачивать в блокировку.

зачем, если _lockDic.AddOrUpdate уже блокирует словарь
на изменение по ключу? пока происходит обновление по
ключу, никто не может по этому же ключу ничего сделать
даже TryGet в параллельном потоке будет ждать
Да, блокирует, но у тебя есть и другие строки в коде. То что ты в коде использовал атомарную операцию не делает весь код атомарным.
Пример
Поток 1Поток 2if(нет объекта в словаре)Поток 1 прерван потоком 2if(нет объекта в словаре)o = объект1AddOrUpdate(o)работаем с объект1o = объект2AddOrUpdate(o)работаем с объект2проболжаем работать с объект1
Понял зачем блокировка?

Остальное позже посмотрю, вечером.
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39360002
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
absintheчто я делаю не так?
Извиняюсь, не вник глубоко, сразу не понял что изнутри AddOrUpdate() ты перепроверяешь и меняешь lockObj. Возможно это атомарно отработает.
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39360007
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Должно работать.

У тебя в коде косяк небольшой
Код: c#
1.
2.
3.
ReaderWriterLockSlim lockObj;
...
      var lockObj = new ReaderWriterLockSlim();


var лишний.

И не помешает вообще избавиться от
Код: c#
1.
2.
      var lockObj = new ReaderWriterLockSlim();
      var lockRef = new WeakReference<ReaderWriterLockSlim>(lockObj);


перенести их в добавление, а в обновлении оставлять старый WeakReference, а внутри него обновлять ссылку на новый ReaderWriterLockSlim.
примерно так
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
public ReaderWriterLockSlim GetLock(string key)
{
   WeakReference<ReaderWriterLockSlim> lockRef;
   ReaderWriterLockSlim lockObj;
   if(!_lockDic.TryGet(key, out lockRef) || (lockObj = lockRef.Target) == null)
   {
      _lockDic.AddOrUpdate(key, (k) => {
         lockObj = new ReaderWriterLockSlim();
         return new WeakReference<ReaderWriterLockSlim>(lockObj);
       }
       , (k, v) => 
      {
          lockObj = v.Target;
          if (lockObj == null)
          {
             lockObj = new ReaderWriterLockSlim();
             v.Target = lockObj;
          }
          return v;
      });
   }
   return lockObj;
}


absintheпочитал больше про WeakReference, пишут что ссылка может
моментально обнуляться, может надо устанавливать trackResurrection
в true, но тут я пока не понял, поможет ли это действительно
продлить жизнь ссылке
ИМХУ в твоем случае это не грозит, т.к. есть строгая ссылка в lockObj
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39360055
Var79
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TМне кажется что будет тормозить при больших нагрузках из-за постоянных new ReaderWriterLockSlim() и удаления их сборщиком мусора, плюс эта блокировка.
Кажется лучше тестировать чем гадать.
Вообще не понимаю зачем итернирование строк, ведь итерированные строки gc убирать не будет.
ТС делал какие то замеры сам, в release режиме?
gc-шного времени не занимает, а то что строки память жрут, кого вообще волнует? Память дешевая.
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39360098
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
absintheкак всетаки оценить какое решение лучше?
По ссылке из первого топика есть какие-то тесты . Их не пробовал запускать?
absinthe я начал писать проект
для тестирования, но возникли трудности создания условий
много потоков должны получать блокировку и нужно обеспечить,
чтобы где-то 10-20% потоков пытались заблокировать один ресурс
одновременно, не получается
Как вариант: сделай глобальную переменную и меняй ее из управляющего потока с какой-то частотой
Код: c#
1.
String main_key;


внутри тестового потока счетчик, каждый пятый/десятый проход работа с main_key
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
Int32 cnt = 0;
String key;
while(cnt < 100500) {
   cnt++;
   if(cnt % 5 == 0) {
     key = main_key;
   } else {
     key = "key " + cnt.ToString();
   }
   ... что-то делаем с key
}


тут тебе еще чтение/запись надо сэмулировать. Как вариант попытка записи у каждого четвертого, т.е. при cnt % 20 == 0
...
Рейтинг: 0 / 0
Блокировка по ключу
    #39360104
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще надо в "что-то делаем с key" добавить какой-нибудь расчет, который будет с разной скоростью идти. Например на вход каждому потоку давать какое-то число (всем потокам разные числа) и внутри "что-то делаем с key" посчитать сумму квадратов от 1 до этого числа.
...
Рейтинг: 0 / 0
14 сообщений из 39, страница 2 из 2
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Блокировка по ключу
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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