Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.77/13: Рейтинг темы: голосов - 13, средняя оценка - 4.77
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146

Как правильно залочить структуру в массиве?

27.08.2019, 17:59. Показов 2833. Ответов 34
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Подскажите, как правильно залочить структуру в массиве?

1) Есть структура. Примерно такая,

C#
1
2
3
4
5
6
7
8
public struct MyEntity 
{
        public ulong data0;
        public ulong data1;
        public ulong data2;
        public ulong data3;
        public ulong data4;
}
2) Есть самописный класс, для создания динамических массивов этих структур.
(Потому и структуру создавал MyEntity , а не класс , чтобы меньше оперативной памяти использовалось.
На каждый объект класса идёт больше памяти ).

Примерно так, (T в данном случае - может быть структура выше, MyEntity)

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
public class DynamicArrayIncreasing<T> where T : struct 
{
     // ... 
 
     public ulong Count { get; protected set; }
     public T[] InternalArray { get; set; }
 
     public void AddOrReplace(ref T item , int index)
     {
          ulong workingVal = 0;
          // ... define workingVal
 
          if (!IsMultiThreading)
          {
                 // ...
          }
          else
          {
                lock(Count)
                {
                    lock (InternalArray[workingVal])
                    {
                         InternalArray[workingVal] = item;
                    }
                    Count++;
                }
          }
     }
 
}

Я пытаюсь залочить два значения - Count - потому что именно Count и перезаписывается,
а также значение InternalArray[workingVal] -- что есть структура из 5-ти полей.

И это логично, ведь если только эти значения будут перезаписываться, то могут быть перезатёрты
потоками, если их не залочить. Т.е. может быть такая ситуация 1-й поток получит InternalArray[workingVal] ,
запишет туда 2 значения, а потом планировщик переключит на другой поток, и он затрёт данные первого потока.

Но в результате получаю ошибку -
is not a reference type as required by the lock statement

Получается, что компилятор не даёт залочить именно эти два значения, потому что это не ссылочные типы.
И что тогда оптимальнее всего в плане производительности - лочить весь DynamicArrayIncreasing --
т.е. на время запрещать другим потокам обращаться ко всему массиву?
Это неоптимально, если потоку нужно только один элемент массива перезаписать, который - есть структура из
5-ти целочисленных значений.

Спасибо.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
27.08.2019, 17:59
Ответы с готовыми решениями:

Как правильно написать функцию, которая редактирует структуру по полю Name и возвращает новую, исправленную структуру?..
Нужно отредактировать структуру по выбору пользователя. Проблема заключается в создании функции. Подскажите, а лучше покажите, как...

Как правильно передать указатель на структуру, и правильно ее использовать
Я планирую сделать сортировку, но компилятор начал ругаться &quot;Нет существует подходящей функции преобразования &quot;Student&quot; в...

Как залочить MX25L8005
Всем привет!! Как залочить микруху MX25L8005. Спрашивал на других сайтах. Ответы типа - Микросхемы SPI Flash ROM, поддерживают сигнал...

34
95 / 74 / 27
Регистрация: 13.08.2018
Сообщений: 203
27.08.2019, 19:10
Если не хотите использовать классы, то для перезаписи структур можно использовать именованную блокировку по индексу.
C#
1
2
3
4
5
6
7
8
using (var mutex = new Mutex(false, index.ToString()))
    {
        mutex.WaitOne();
 
        //write file
 
        mutex.ReleaseMutex();
    }
1
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
27.08.2019, 19:19  [ТС]
Если не хотите использовать классы, то для перезаписи структур можно использовать именованную блокировку по индексу
Спасибо. А вот ещё нашёл, что Monitor.Enter + Monitor.Exit не требует ссылочного типа.

Т.е. код выше переписывается так и компилируется , если lock заменить на Monitor.Enter + Monitor.Exit ,

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//...
          if (!IsMultiThreading)
          {
                 // ...
          }
          else
          {
                Monitor.Enter(Count);
                {
                    Monitor.Enter  (InternalArray[workingVal])
                    {
                         InternalArray[workingVal] = item;
                    }
                    Monitor.Exit(InternalArray[workingVal]);
 
                    Count++;
                }
                Monitor.Exit(Count);
          }
//...
Правда, не знаю , подходит ли, и будет ли выигрыш в производительности,

1) по сравнению с обычными lock- когда блокируется весь массив,

2) по сравнению с вашим примером, с Mutex .
С Mutex я раньше не работал, вы думаете, что пример с Mutex - наиболее быстрый ? (а скорость важна).
0
95 / 74 / 27
Регистрация: 13.08.2018
Сообщений: 203
27.08.2019, 22:28
Спасибо. А вот ещё нашёл, что Monitor.Enter + Monitor.Exit не требует ссылочного типа.
C#
1
public static void Enter(object obj);
А object это разве не ссылочный тип ?
Вы передаете в метод копию структуры, которая будет упакована в ссылочный тип. Смысла от такой блокировки нет вообще.
1
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
28.08.2019, 11:50  [ТС]
Сначала, хочу разобраться с этими lock- разных типов.
Правильно ли я понимаю?


Захватывается object monitor указанного объекта (это может быть и this). Теперь потоки, пытающиеся тоже получить object monitor того же объекта, не получат его, пока исполняется наша критическая секция. То есть, никакой "блокировки объектов" не существует — будет запрещен доступ только в те места, где пытаются получить монитор уже "захваченного" объекта.

1) есть возможность залочить весь объект ( lock {} или что эквивалентно, lock(this) {}), где выполняется данный код, т.е. на время выполнения этого кода в lock - это нехорошо тем, что все другие участки кода, с таким же, lock(this) {} -- выполняться в это время не будут.

2) есть возможность просто объявить критическую секцию, а для этого - создать сцециальный объект для неё и залочить его (в C++ была такая возможность записать некий код в CRITICAL_SECTION, возможно даже ОС, сама создавала некий объект для этого). Опять же, это нехорошо тем, что в данном случае, потоки не смогут писать любые данные , т.е. в этот блок кода не зайдут, а другим потокам, возможно, в этот момент требуются записи совсем в другой элемент массива.


1-й и 2-й случаи показаны в коде ниже --

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
44
45
46
47
48
49
50
51
52
53
54
    class LockTest
    {
        public void foo()
        {
            // lock (null) -- видимо смысла не имеет, т.к. в C# любой lock связан 
            // с объектом, а как просто объявить критическую секцию - 
            // код ниже с созданием спец-объекта.
             
            lock {  // эквивалентно lock(this)
 
                // допустим, один поток находится здесь
                // здесь код критической секции + залочен весь объект this
            }
        }
        public void foo1()
        {
            lock (this)
            {
                // а вот сюда другой поток не попадёт, 
                // т.к. монитор this уже выше захвачен в foo()  ! 
                // Именно это показывает что 1) вариант lock (this) - неоптимальный
            }
        }
 
        public void OtherMethod()
        {
            // другой (или много) потоков могут выполнять этот метод. 
            // Объект this залочен, но этот метод НЕ находится в блокируемом участке
        }
 
        private object foo2Lock = new object(); // спец-объект для критической секции 
        public void foo2()
        {
            lock (foo2Lock)
            {
                // третий поток может выполнять этот метод.  Но четвертый - не может,
                // пока третий не выйдет из этого блока. 
                // Т.е.,  foo2 одновременно может исполняться не более чем одним потоком. 
                // Это - 2-й вариант -- аналог C++ критической секции, и общий 
                // object foo2Lock -- был специально для неё и создан. 
 
                //  Если здесь пытаться переписывать элемент массива (как в классе, 
                //  с показанным кодом выше), т.е. 
                //
                //  InternalArray[workingVal] = item;
                // 
                //  то другие потоки, в это время не смогут переписывать элементы 
                //  массива структур, с другим  workingVal - именно с этом 
                //  неоптимальность этого, 2-го варианта! 
                // Хотя он лучше чем 1-й вариант  с lock (this) выше. 
 
            }
        }
    }

3) делать упаковку структуры (элемента массива) в некий объект класса и в lock(ob) - указывать этот объект - смысл получается тот же, как во 2- примере выше, т.к. этот объект будет общий для всех потоков, и никак не будет связан со структурой которую хотим залочить.

4) делать упаковку структуры (элемента массива) в некий локальный объект метода, и в lock(ob) - указывать этот объект - смысла вообще никакого нет, т.к. у всех потоков будут разные залоченные объекты, и по сути, никак потоки не будут влиять друг на друга. Т.е. это эквивалентно тому, как если бы вообще ничего не лочили.

Таким образом получается, что массив создавал со структурами, а не объектами классов, чтобы памяти меньше
использовалось. Но зато нельзя при таком подходе залочить именно элемент массива для критической секции.
Можно создать параллельно в том же классе, массив объектов, которые будут - упакованные int, и лочить их -
но тогда пропадёт наш выигрыш по используемой памяти - ведь в таком случае, всё равно будут присутствовать
N объектов ссылочного типа. (массив из N структур нессылочного типа + массив N объектов ссылочного типа -
с таким результатом можно было и сразу N объектов вместо N структур использовать ).

C Mutex пока ещё не разобрался. Неужели, позволяет использовать аналоги критических секций как
в примере выше, и при этом не надо создавать никаких объектов..
0
95 / 74 / 27
Регистрация: 13.08.2018
Сообщений: 203
28.08.2019, 12:37
Не видя весь код, трудно что то подсказать. Можно например хранить индексы с которыми ведется работа в массиве, а потоки в залоченом методе будут их проверять, и действовать по ситуации. Думаю это будет лучшим вариантом.
1
 Аватар для Cupko
658 / 595 / 171
Регистрация: 17.07.2012
Сообщений: 1,682
Записей в блоге: 1
28.08.2019, 12:50
Цитата Сообщение от SergeyYN Посмотреть сообщение
1) есть возможность залочить весь объект ( lock {} или что эквивалентно, lock(this) {}), где выполняется данный код, т.е. на время выполнения этого кода в lock - это нехорошо тем, что все другие участки кода, с таким же, lock(this) {} -- выполняться в это время не будут.
2) есть возможность просто объявить критическую секцию, а для этого - создать сцециальный объект для неё и залочить его (в C++ была такая возможность записать некий код в CRITICAL_SECTION, возможно даже ОС, сама создавала некий объект для этого). Опять же, это нехорошо тем, что в данном случае, потоки не смогут писать любые данные , т.е. в этот блок кода не зайдут, а другим потокам, возможно, в этот момент требуются записи совсем в другой элемент массива.
Цитата Сообщение от SergeyYN Посмотреть сообщение
Таким образом получается, что массив создавал со структурами, а не объектами классов, чтобы памяти меньше
использовалось. Но зато нельзя при таком подходе залочить именно элемент массива для критической секции.
Вы лочите не объект, вы лочите как раз таки критическую секцию объектом ссылочного типа. Поэтому я слабо понимаю что вы хотите получить обернув в lock код доступа к элементу массива.
Цитата Сообщение от SergeyYN Посмотреть сообщение
3) делать упаковку структуры (элемента массива) в некий объект класса и в lock(ob) - указывать этот объект - смысл получается тот же, как во 2- примере выше, т.к. этот объект будет общий для всех потоков, и никак не будет связан со структурой которую хотим залочить.
Цитата Сообщение от SergeyYN Посмотреть сообщение
4) делать упаковку структуры (элемента массива) в некий локальный объект метода, и в lock(ob) - указывать этот объект - смысла вообще никакого нет, т.к. у всех потоков будут разные залоченные объекты, и по сути, никак потоки не будут влиять друг на друга. Т.е. это эквивалентно тому, как если бы вообще ничего не лочили.
Весь смысл как раз в том, чтобы иметь общий объект для секции. Чтобы потоки были в курсе, зашел ли какой-либо поток в секцию или нет.
Да при любом подходе нельзя. Вы синхронизируете доступ к элементу массива а не сам элемент массива. Вам явно нужен другой подход.
Цитата Сообщение от SergeyYN Посмотреть сообщение
C Mutex пока ещё не разобрался. Неужели, позволяет использовать аналоги критических секций как
в примере выше, и при этом не надо создавать никаких объектов..
С Mutex вообще тема отдельная, ибо в примере выше используются именованные мьютексы, которые не ограничиваются вашим приложением и может уже быть использован в стороннем процессе.

Добавлено через 8 минут
UPD: Всё, окей, дошло. Используйте объекты ссылочного типа и будет вам счастье. Что вы там по памяти сэкономить хотите?
И для инкремента Count есть уже готовая штука в виде Interlocked.Increment();
1
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
28.08.2019, 13:55  [ТС]
Перед тем как разбираться с Mutex, ещё посмотрел, можно ли здесь использовать синхронизацию,
вообще без блокировок. Если операция атомарная, например чтение-запись типа int - это можно сделать,
правда надо использовать Ключевое слово volatile к этому int.
VOLATILE м INTERLOCKED .

VOLATILE . Ключевое слово volatile означает, что поле может изменить несколько потоков, выполняемых
одновременно. Компилятор, среда выполнения или даже аппаратное обеспечение могут изменять порядок операций
чтения и записи в расположения в памяти для повышения производительности.
К полям, которые объявлены как volatile, такие оптимизации не применяются.
Т.е. в случае записи процессор не запишет в кэш ЦП, а запишет сразу в ОП (изменение переменной станет видно
всем другим потокам), и т.д.
Ключевое слово volatile может применяться к полям следующих типов:
Ссылочные типы. (насколько я понял, потому что сам компилятор применит некую блокировку чтобы потоки
выполнили как АТОМАРНУЮ операцию --> read-write ссылок на объекты, которые могут быть 8-байтовыми).
также, Простые типы, исключая double и long - тогда ключевое слово volatile по сути,
позволяет сделать неблокирующую синхронизацию между потоками, потому что операции могут быть выполнены,
как атомарные.
Структуры, и типы double и long, нельзя снабдить модификатором volatile, потому что для них не гарантируется
атомарность операций чтения и записи. Чтобы защитить многопотоковый доступ к полям таких типов,
нужно использовать члены класса Interlocked или защиту доступа с помощью инструкции lock.
lock - выше рассмотрел, не вариант в плане производительности.

INTERLOCKED -- Надо рассмотреть как использовать . C double и long можно использовать, а можно ли
применить к струтктуре, создержащей просто 5 простых полей типа long , и баз блокировок добиться
аналога атомарности чтения-записи ? С этим пока не разобрался.


Поэтому я слабо понимаю что вы хотите получить обернув в lock код доступа к элементу массива.
Чтобы в этот момент, другие потоки могли входить в этот же код, и могли переписать другие элементы массива,
т.е. другие структуры. Но именно структуру, которую переписывает какой то поток в этот момент -
другие потоки не могли переписать.


Можно например хранить индексы с которыми ведется работа в массиве, а потоки в залоченом методе будут
их проверять,
Хорошая идея, спасибо.

5) вариант. Хранить индекс переписываемого элемента массива.
Но всё равно не идеальный.
Допустим, есть 20 потоков. Иногда они переписывают какой то элемент в массиве.
Перед тем как переписать - поток просто записывает int индекс массива, элемент которого (структуру ) он собрался переписать.
Операция атомарная. Всё вроде, ок. Поток переписал структуру в массиве, после чего установил индекс в -1 -
т.е. ничего не переписывает .
Но, если мы будем хранить только 1 индекс, в момент
перезаписи элемента массива, тогда пока поток переписывает свой элемент в массиве, другие потоки не смогут
переписать другие элементы (хотя 1-й поток в данном случае не мешает) .
Хранить же 20 индексов смысла нет, т.к. появляется проверка по 20-ти int, что уже само по себе не атомарная операция.

Идеальный же вариант был бы - добиться атомарности перезаписи структуры (так 5 длинных интов всего!)
и ничего не лочить..

Используйте объекты ссылочного типа и будет вам счастье. Что вы там по памяти сэкономить хотите?
Очень даже многое. В примере - в массиве используются структуры, длиной в 5 ulong
для переборной алгоритмической задачи (на самом деле их позже, может быть больше 5-ти, но не суть).
Это - 5 умножить на 8 == равно 40 байт на элемент в массиве.
Длина массива может доходить на миллиарда элементов, и память закончится.

Используйте вместо структуры - объекты class (т.е. ссылочные) - и вы получите не 40 байт на элемент в массиве,
а где то (уже не помню, равньше когда то тестил) - а чуть ли не в 2 раза больше.
И в переборной задаче, мы сможем хранить в 2 раза меньше элементов.

Добавлено через 20 минут
С Mutex вообще тема отдельная,
Что у Mutex в плане производительности?

Добавлено через 16 минут
Ещё заметил, что

C#
1
2
3
4
5
6
//...
                    lock (InternalArray[workingVal])
                    {
                         InternalArray[workingVal] = item;
                    }
//...
лочить только запись, не совсем корректно.
Нужно лочить ещё и чтение. Если 1-й поток пытается что то записать по этому элементу, с индексом workingVal,
то 2-й поток, пытаясь записать - сюда не зайдёт. Но 3-й поток, который где то в другом месте, читает этот элемент, с индексом workingVal ,
(а там структура с 5-ю длинными интами).
может прочитать 2 инта старые, и 3 перезаписанные например.

Поэтому, лочить нужно, и запись и чтение, именно поэлементно, т.е. для одного элемента массива.
Т.е. , лочить нужно и чтение.
Либо делать операции чтения-записи, для этой структуры как атомарные, (как если бы там один int всего был.
Вот для одного такого int определенного со словом volatile - задача была бы решена быстро и без всяких блокировок).
0
95 / 74 / 27
Регистрация: 13.08.2018
Сообщений: 203
28.08.2019, 14:11
SergeyYN, а если работать с массивом указателей на структуры , не устроит ? Указатели можно атомарно переписывать.
0
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
28.08.2019, 14:24  [ТС]
а если работать с массивом указателей на структуры , не устроит ? Указатели можно атомарно переписывать.
maximka777, в смысле, использовать указатели в unsafe коде?

В памяти массив записан как бы, в линейном блоке , так сказать.
Т.е. каждая следующая структура находится после предыдущей. (и массив выделялся единым блоком, т.е. одним new).
Если каждую структуру выделять в новом месте памяти с помощью new - это ухудшение производительности.
У меня что? В методе, прямо в стеке - определена структура, я только вычисляю записываю её данные (в данном случае 5 длинных int). А потом нахожу нужный элемент в массиве структур, и эти 5 длинных int - перезаписываю в структуру-элемент в массиве.

Если же каждую структуру хранить в heap разных местах, то придётся выделять для каждой новой структуры память - с помощью new - это очевидно, доп. нагрузка,
как минимум на подсистему, занимающуюся организацией и распределением heap (кучи).

Хотя в таком случае да, у меня был бы массив указателей в этом DynamicArray классе, и любой указатель можно было
бы залочить, тем самым добившись атомарности перезаписи данных в структуре.
0
1123 / 794 / 219
Регистрация: 15.08.2010
Сообщений: 2,185
28.08.2019, 14:31
Цитата Сообщение от SergeyYN Посмотреть сообщение
Таким образом получается, что массив создавал со структурами, а не объектами классов, чтобы памяти меньше
использовалось.
какой выигрыш в памяти вы ожидаете и стоит ли игра свеч?
почему не учитываете выигрыш в скорости обращения от использования класса вместо структуры?
Цитата Сообщение от SergeyYN Посмотреть сообщение
lock - выше рассмотрел, не вариант в плане производительности.
lock не является заменой volatile или чем либо подобным.
про производительность опять же сначала нужен аргумент в пользу структуры вместо класса
Цитата Сообщение от SergeyYN Посмотреть сообщение
Ещё заметил, что
это тот самый момент когда надо сесть с бумажкой и решить, что вы хотите от класса, отпределить инварианты и внешний интерфейс. Потом изложить это здесь четко и ясно, тогда можно думать. Пока лично мне не очень понятно, зачем это все и что в итоге то надо?
0
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
28.08.2019, 14:35  [ТС]
Хотя в таком случае да, у меня был бы массив указателей в этом DynamicArray классе, и любой указатель можно было
бы залочить, тем самым добившись атомарности перезаписи данных в структуре.


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

Добавлено через 3 минуты
КОП
какой выигрыш в памяти вы ожидаете и стоит ли игра свеч?
Выше уточнял. Выигрыш по памяти огромный. и более чем в 2 раза.

В примере - в массиве используются структуры, длиной в 5 ulong
для переборной алгоритмической задачи (на самом деле их позже, может быть больше 5-ти, но не суть).
Это - 5 умножить на 8 == равно 40 байт на элемент в массиве.
Длина массива может доходить на миллиарда элементов, и память закончится.

Используйте вместо структуры - объекты class (т.е. ссылочные) - и вы получите не 40 байт на элемент в массиве,
а где то (уже не помню, равньше когда то тестил) - а чуть ли не в 2 раза больше.
И в переборной задаче, мы сможем хранить в 2 раза меньше элементов.


Именно экономия памяти на больших массивах - и есть аргумент в пользу структуры вместо класса.
0
95 / 74 / 27
Регистрация: 13.08.2018
Сообщений: 203
28.08.2019, 14:36
Цитата Сообщение от SergeyYN Посмотреть сообщение
В памяти массив записан как бы, в линейном блоке , так сказать.
Т.е. каждая следующая структура находится после предыдущей. (и массив выделялся единым блоком, т.е. одним new).
Если каждую структуру выделять в новом месте памяти с помощью new - это ухудшение производительности.
У меня что? В методе, прямо в стеке - определена структура, я только вычисляю записываю её данные (в данном случае 5 длинных int). А потом нахожу нужный элемент в массиве структур, и эти 5 длинных int - перезаписываю в структуру-элемент в массиве.
Если же каждую структуру хранить в heap разных местах, то придётся выделять для каждой новой структуры память - с помощью new - это очевидно, доп. нагрузка,
как минимум на подсистему, занимающуюся организацией и распределением heap (кучи).
Дак все сразу и выделить можно разом. Просто в каждом потоке создать одну структуру, для записи и свапа указателя.
0
28.08.2019, 14:41

Не по теме:

Цитата Сообщение от SergeyYN Посмотреть сообщение
Выше уточнял.
пардон, видимо давно не обновлял страничку)

0
 Аватар для Cupko
658 / 595 / 171
Регистрация: 17.07.2012
Сообщений: 1,682
Записей в блоге: 1
28.08.2019, 14:45
Цитата Сообщение от SergeyYN Посмотреть сообщение
Очень даже многое. В примере - в массиве используются структуры, длиной в 5 ulong
для переборной алгоритмической задачи (на самом деле их позже, может быть больше 5-ти, но не суть).
Это - 5 умножить на 8 == равно 40 байт на элемент в массиве.
Длина массива может доходить на миллиарда элементов, и память закончится.
Используйте вместо структуры - объекты class (т.е. ссылочные) - и вы получите не 40 байт на элемент в массиве,
а где то (уже не помню, равньше когда то тестил) - а чуть ли не в 2 раза больше.
И в переборной задаче, мы сможем хранить в 2 раза меньше элементов.
Я про саму структуру, а не поля в ней. Объект класса занимает на 4/8 байт больше чем структура.
У вас в любом случае память закончится, на миллиардном массиве или на миллиард сто.
0
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
28.08.2019, 14:58  [ТС]
maximka777
Дак все сразу и выделить можно разом. Просто в каждом потоке создать одну структуру, для записи и свапа указателя.
Имеете в виду, что в DynamicArray классе - храним 1) выделенный массив структур (как у меня в 1-м примере),

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
public class DynamicArrayIncreasing<T> where T : struct 
{
     // ... 
 
     public ulong Count { get; protected set; }
     public T[] InternalArray { get; set; }
 
     public pointer[] InternalPointers { get; set; }  // pointer - указатель на структуру длиной 8 байт
 
     public void AddOrReplace(ref T item , int index)
     {
          ulong workingVal = 0;
          // ... define workingVal
 
          if (!IsMultiThreading)
          {
                 // ...
          }
          else
          {
                //  блок Add.. 
 
                // блок Replace --> 
                lock (InternalArray[workingVal])
                {
                       InternalArray[workingVal] = item;
                }
                
          }
     }
 
        public T this[ulong index]
        {
            // ... define workingVal 
            get
            {
                return InternalArray[workingVal];
            }
        }
 
}
но реально, i-й элемент DynamicArrayIncreasing отвечающего за массив, т.е. this[i] - в коде выше это вовсе не
i-й элемент во внутреннем "линейном массиве структур", т.е. не
InternalArray[i] ,

а некий InternalArray[workingVal] , где workingVal -- переопределяемый номер в зависимости от массива указателей.
Тогда, дополнительно

1) Массив указателей, на структуры дополнительно добавит в память, только на +8 байт, на одну доп. сущность в DynamicArrayIncreasing . (что не так существенно, по сравнению со случаем хранения объектов класса вместо структур),

2) структуры , перезаписываются в InternalArray[index] , только для тех index, которых временно нет массиве указателей,
pointer[] InternalPointers ?

Я правильно понял? И тогда будет достагнута цель - атомарность перезаписи структур, без всяких lock,
и без использования ссылочных типов.

Если это прокатит, то вариант просто гениальный по своей задумке.
Но я ещё не всё продумал по этому поводу, т.е. голова уже кругом идёт.. Могу ошибаться.

Cupko ,
Добавлено через 2 минуты
Объект класса занимает на 4/8 байт больше чем структура.
Возьмите, и создайте массив структур, вот таких -

C#
1
2
3
4
5
6
7
8
public struct MyEntity 
{
        public ulong data0;
        public ulong data1;
        public ulong data2;
        public ulong data3;
        public ulong data4;
}
Чтобы они заняли у вас в памяти 1 ГБ. А потом замените слово struct на class, и создайте такой же массив по числу элементов.
Тогда и будет видно, что совсем не +8 байт добавляется на ссылочный тип по сравнению со структурой.
А намного больше.
0
1123 / 794 / 219
Регистрация: 15.08.2010
Сообщений: 2,185
28.08.2019, 15:39
Цитата Сообщение от Cupko Посмотреть сообщение
Объект класса занимает на 4/8 байт больше чем структура.
4 на синк блок, 4/8 на хедер, 4/8 на саму ссылку на объект. + погрешности выравнивания и имплементации рантайма.(у меня аккурат 20 под х64)
Цитата Сообщение от SergeyYN Посмотреть сообщение
Массив указателей, на структуры
может все таки на плюсах написать? я серьезно
Цитата Сообщение от SergeyYN Посмотреть сообщение
только для тех index, которых временно нет массиве указателей,
pointer[] InternalPointers
хотите добавить линейный поиск и синхронизацию массива для синхронизации?
2
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
28.08.2019, 15:53  [ТС]
Ладно, я вот сейчас сам перепроверил. Это для x64 платформы (в студии указал).

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
44
  
 
public interface ISizeDefinable
{
        ulong SizeOf();
}
  
 
// здесь используем struct  или class 
 
public struct MyEntity : ISizeDefinable
    {
        public ulong data0;
        public ulong data1;
        public ulong data2;
        public ulong data3;
        public ulong data4;
 
        public ulong SizeOf()
        {
            return 40UL;    // размер в байтах 
        }
    }
 
//  затем создаём объект 
 
MyEntity[] ent = null; //
 
private void btnBuild_Click(object sender, EventArgs e)
{
            // если структура (закомментируем всё кроме строки ниже), то Commit size  
            // в  Task Manager-e  ==  800500 КБ, что 40  байт на 1 структуру класса MyEntity
            MyEntity[] ent = new MyEntity[20000001];
 
 
            // если класс (закомментируем строку выше, откомментируем все ниже) , то Commit size
            //   в   ~Task Manager-e ==  ~1297000 КБ, что 66  байт на 1 объект класса MyEntity
            ent = new MyEntity[20000001];
            for (int i = 0; i < 20000001; i++ )
            {
                ent[i] = new MyEntity();
                ent[i].data4 = 22;
            }
}
Уже при создании видим , доп расходы на объекты класса по сравнению со структурой, +26 байт. (66 против 40).

Но это в идеальном случае, только массив создали и объекты. При всякой работе же, с этими объектами, если MyEntity -
не структура, а класс, то доп. расходы в системе могут доходить до +30 байт на каждый элемент в массиве!

Это значит что? Всего элементов в массиве я смогу хранить почти в 2 раза меньше.

КОП
Добавлено через 2 минуты
может все таки на плюсах написать? я серьезно
C# гораздо более простой язык, и с бОльшими возможностями чем C++.
А C++ я уже работал раньше, и знаю, какие там трудности.

Добавлено через 4 минуты
КОП
хотите добавить линейный поиск и синхронизацию массива для синхронизации?
Я ещё не обдумал этот момент (только поверхностно).
Вот же, maximka777 , вкратце написал,

Дак все сразу и выделить можно разом. Просто в каждом потоке создать одну структуру, для записи и свапа указателя.
Пытаюсь понять, что имел в виду.
0
95 / 74 / 27
Регистрация: 13.08.2018
Сообщений: 203
28.08.2019, 16:04
Цитата Сообщение от SergeyYN Посмотреть сообщение
Это значит что? Всего элементов в массиве я смогу хранить почти в 2 раза меньше.
Можно запихать в класс по 8 структур и перегрузить индексатор, + 12,5 % большой роли не сыграют, будет золотая середина.
1
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
28.08.2019, 16:05
Цитата Сообщение от SergeyYN Посмотреть сообщение
5 умножить на 8 == равно 40 байт на элемент в массиве.
Длина массива может доходить на миллиарда элементов, и память закончится.
При использовании структур у вас память закончится намного быстрее, чем при использовании классов — банально из-за того, что для массива структур потребуется больше памяти, чем для массива ссылок.

Ну и миллиард элементов на 40 байт каждый — это минимум 40 миллиардов байт или 40 ГБ памяти.
Вне зависимости от того, где они хранятся, вам придется пересмотреть подход к работе с такими объемами.

Что касается синхронизации, то поскольку вы синхронизируетесь на Count, постольку внутренняя синхронизация на элементе массива смысла не имеет, т.к. нет ситуации, при которой эту проверку выполняло бы больше одного потока.
Так что сделайте один закрытый объект для синхронизации и используйте только его:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     private readonly object _sync = new object();
     public void AddOrReplace(ref T item , int index)
     {
          ulong workingVal = 0;
          // ... define workingVal
 
          if (!IsMultiThreading)
          {
                 // ...
          }
          else
          {
                lock(_sync)
                {
                    InternalArray[workingVal] = item;
                    Count++;
                }
          }
     }
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
28.08.2019, 16:05
Помогаю со студенческими работами здесь

Как правильно определить структуру?
Доброго всем времени! У меня возник вопрос по структурам: .data WORKER struc ;информация о сотруднике nam db 30 dup (' ') ...

Как правильно описать структуру БД?
Я делаю чат и мне понадобилась БД для списка пользователей. Там будут в частности администраторы и баны. Я использую Entity Framework и...

Как правильно объявить структуру
Есть вот такая вот структурка: typedef struct { // 1st byte (event code) union { struct { ...

Как правильно объявить структуру
Всем доброго времени суток.Есть задача:Составить программу формирования данных об успеваемости студентов по дисциплине: фамилия И.О., 5...

Как правильно определить структуру
код в модуле: typedef struct { BaseType *Buf; // Массив элементов базового типа unsigned Kbuf; /*...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Рецензия / Мнение/ Перевод Нашел на реддите интересную статью под названием The Thinkpad X220 Tablet is the best budget school laptop period . Ниже её машинный перевод. Thinkpad X220 Tablet —. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru