Гость
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Хитрая очередь с приоритетом без блокировок / 25 сообщений из 50, страница 1 из 2
25.06.2018, 13:33
    #39665314
SergASh
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Привет всем!

Имеется последовательность элементов вот такого вида:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
class Item
{
  int ID;
  decimal Level;
  decimal Measurement1;
  ...
  decimal MeasurementN;
}



Нужно придумать структуру данных для хранения этой последовательности,
чтобы она обслуживала вот такой алгоритм:

Есть два потока - обновлятель и читатель.

Читатель должен иметь возможность
1. Наблюдать эту последовательность всегда упорядоченной по Level
2. Безопасно итерировать по ней без блокировок

Обновлятель должен уметь
1. вставлять новые элементы. Что есть новый и что не новый определяется по Id
2. удалять существующие
3. обновлять измеренные значения. В идеале и Level тоже, но если не получится - переживу
4. избегать копирования больших объемов данных при вставках и удалениях
5. по возможности выполнять перечисленные операции за логарифмическое время

Изменения происходят со скоростью 10^2 в секунду. Длина последовательности порядка 10^4.
Несколько независимых экземпляров этого алгоритма должны работать в одном процессе с
разными, никак не связанными экземплярами последовательностей. Экземпляров этих будет
порядка 10^1.

Читатель просматривает последовательность несколько раз в секунду. Важно, чтобы он всегда
просматривал ее в порядке убывания Level. Допустимо, что во время просмотра обновлятель
что-то поменяет. Главное, чтобы это обновление не обрушило читателю foreach и не привело
к тому, что порядок просмотра перестанет быть упорядочен по Level.

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

Язык реализации C#.

Спасибо
...
Рейтинг: 0 / 0
25.06.2018, 13:46
    #39665324
Cat2
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
SergASh1. Наблюдать эту последовательность всегда упорядоченной по Level
А где "эта" последовательность?

Measurement1-MeasurementN ? Как она может быть упорядочена по Level?
...
Рейтинг: 0 / 0
25.06.2018, 13:47
    #39665326
Cat2
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Извините вопрос снимается. Невнимательно прочитал условие
...
Рейтинг: 0 / 0
25.06.2018, 13:56
    #39665334
Cat2
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
SergASh,

попробуйте ConcurrentBag<T> из

https://msdn.microsoft.com/library/Dd997305(v=VS.100).aspx?f=255&MSPPError=-2147217396

orderby для просмотра можно любой сделать
...
Рейтинг: 0 / 0
25.06.2018, 14:25
    #39665355
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Cat2,

задание мне тут видится академическое и важно сама реализация такой последовательности.
как я понял из задания, для многопоточности использовать надо ReaderWriterLockSlim
в качестве коллекции использовать LinkedList
Чтоб коллекция всегда была сортированной, нам нужно найти то место, куда вставляем элемент, находим такой элемент через бинарный поиск у которого сложность как раз O(log(n)) и вставляем. Всё остальное, точно так же.
и т.д. и т.п. бесплатное такое точно делать не будут.
...
Рейтинг: 0 / 0
25.06.2018, 14:40
    #39665372
Petro123
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
SergASh2. Безопасно итерировать по ней без блокировок

Roman Mejtes,
А п.п. выше как будет работать без блокировок?
...
Рейтинг: 0 / 0
25.06.2018, 15:11
    #39665391
SergASh
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Cat2SergASh,

попробуйте ConcurrentBag<T> из

orderby для просмотра можно любой сделать

orderby означает копирование всего контейнера и потом сортировку. Это, мягко говоря, не очень эффективно
...
Рейтинг: 0 / 0
25.06.2018, 15:15
    #39665396
Petro123
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
SergASh,
Для foreach блокировка по любому нужна при изменении. Неважно на каком уровне кода.
Imho
...
Рейтинг: 0 / 0
25.06.2018, 15:24
    #39665401
Cat2
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
SergAShCat2SergASh,

попробуйте ConcurrentBag<T> из

orderby для просмотра можно любой сделать

orderby означает копирование всего контейнера и потом сортировку. Это, мягко говоря, не очень эффективно
Так как раз и получится "версионная" изолированность обработки, что вроде и надо автору
SergAShДопустимо, что во время просмотра обновлятель
что-то поменяет. Главное, чтобы это обновление не обрушило читателю foreach и не привело
к тому, что порядок просмотра перестанет быть упорядочен по Level.
...
Рейтинг: 0 / 0
25.06.2018, 16:48
    #39665456
fkthat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Во время foreach коллекцию менять нельзя в принципе - это в сам .NET встроено. Будет иксепшен.
...
Рейтинг: 0 / 0
25.06.2018, 20:58
    #39665584
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
смысл в том, чтоб вставлять элементы с учётом их положения после сортировки. Так как список изначально (по заданию) сортирован для него доступен бинарый поиск, который соответствующий "логорифмической сложности" O(log2n) что соответствует заданию
Нужно найти положение элемента и вставить его через Insert
в List<T>
Вот примерный, вариант для вставки элемента в коллекцию, с учётом сортировки

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
        static void Main(string[] args)
        {
            var list = new List<int>(new int[] { 1, 2, 3, 5, 8, 10 });
            int insertItem = 9;
            var index = list.BinarySearch(insertItem);
            if (index < 0)
            {
                list.Insert(-index - 1, insertItem);
            }
            Console.WriteLine(index);
            Console.WriteLine(string.Join(",", list));
            Console.ReadKey();
        }
...
Рейтинг: 0 / 0
25.06.2018, 21:01
    #39665586
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Roman Mejtes
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
        static void Main(string[] args)
        {
            var list = new List<int>(new int[] { 1, 2, 3, 5, 8, 10 });
            int insertItem = 9;
            var index = list.BinarySearch(insertItem);
            if (index < 0)
            {
                list.Insert(~index, insertItem);
            }
            Console.WriteLine(index);
            Console.WriteLine(string.Join(",", list));
            Console.ReadKey();
        }


слегка подправил
...
Рейтинг: 0 / 0
25.06.2018, 22:13
    #39665614
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
В данном примере идет вставка с задержкой в 10 мс (10^2 в секунду) элементов MyStruct, генерируются значения случ. образом.
Каждую секунду коллекция перебирается, подсчитывается её размер и суммарный Measurement и отображается на экран
В коллекции _list элементы всегда в обратной сортировке по Level, по этому читая итератором всегда нужная последовательность.
Добавление\Обновление происходит с O(log2n)
Пример на коленке сделан, сильно не судите строго

Код: 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.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    public struct MyStruct
    {
        public MyStruct(int id, decimal level, decimal measurement)
        {
            Id = id;
            Level = level;
            Measurement = measurement;
        }

        public int Id;
        public decimal Level;
        public decimal Measurement;
    }

    class Program
    {
        static Test _test;
        static void Main(string[] args)
        {
            _test = new Test();

            var tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;
            var task1 = Task.Run(() =>
            {
                while (!token.IsCancellationRequested)
                {
                    OnWrite();
                    Task.Delay(10).Wait();
                }
            });
            var task2 = Task.Run(() =>
            {
                while (!token.IsCancellationRequested)
                {
                    OnRead();
                    Task.Delay(1000).Wait();
                }
            });

            Console.ReadKey();
            tokenSource.Cancel();
            Task.WaitAll(task1, task2);
            foreach (var i in _test.Read())
            {
                Console.WriteLine($"({i.Id:00};{i.Level:0.0000};{i.Measurement:0.0000})");
            }
            Console.ReadKey();
        }

        private static void OnRead()
        {
            decimal result = 0;
            var count = 0;
            foreach (var i in _test.Read())
            {
                result += i.Measurement;
                count++;
            }
            Console.SetCursorPosition(0, 0);
            Console.Write($"{count},{result}");
        }

        static Random _rnd = new Random(0);

        private static void OnWrite()
        {
            var id = _rnd.Next(0, 100);
            var level = (decimal)_rnd.NextDouble();
            var measure = (decimal)_rnd.NextDouble();
            var insertItem = new MyStruct(id, level, measure);
            _test.InsertOrUpdate(insertItem);
        }

        public class Test
        {

            private readonly List<MyStruct> _list;
            private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

            public Test()
            {
                _list = new List<MyStruct>();
            }

            public void InsertOrUpdate(MyStruct item)
            {
                _lock.EnterUpgradeableReadLock();
                var index = _list.BinarySearch(item, Comparer<MyStruct>.Create((x, y) => y.Level.CompareTo(x.Level)));
                _lock.EnterWriteLock();
                if (index < 0)
                {
                    _list.Insert(~index, item);
                }
                else
                {
                    _list[index] = item;
                }
                _lock.ExitWriteLock();
                _lock.ExitUpgradeableReadLock();
            }

            public IEnumerable<MyStruct> Read()
            {
                _lock.EnterReadLock();
                foreach (var i in _list)
                {
                    yield return i;
                }
                _lock.ExitReadLock();
            }
        }
    }
}
...
Рейтинг: 0 / 0
26.06.2018, 09:32
    #39665711
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Roman Mejtes, внутри List обычный массив, как следствие вставка и удаление требуют сдвига всех последующих элементов, что не быстро и чем ближе к началу - тем медленнее.
...
Рейтинг: 0 / 0
26.06.2018, 11:26
    #39665848
ВМоисеев
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
>SergASh, вчера, 13:33 http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1296919&msg=21518500][21518500]
>… Есть два потока - обновлятель и читатель.
Рассмотри вариант, когда обновлятель измерения пишет не в основной список, а в промежуточный циклический массив.
Читатель с нужной для него скоростью сначала обрабатывает циклический массив с переписью информации в основной список, а уж затем сканирует основной список.
...
Рейтинг: 0 / 0
26.06.2018, 12:46
    #39665941
refreg
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
fkthatВо время foreach коллекцию менять нельзя в принципе - это в сам .NET встроено. Будет иксепшен.А если свой Enumerator реализовать?
...
Рейтинг: 0 / 0
26.06.2018, 12:54
    #39665954
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Dima T,

В List<T> для получения элемента коллекции по индексу требует O(1), а у LinkedList O(n).
Вставка в массив это часть алгоритма и она входит в (n), я пренебрегаю данный работой и считаю её за N.
То есть на требования задачи это не влияет.
А вообще есть класс SortedSet<T>, в нём сразу реализовано бинарное дерево и всё остальное, можно использовать его. По сути тоже самое. B для вставки, удаления и извлечения объекта там сложно O(log n)
...
Рейтинг: 0 / 0
26.06.2018, 15:20
    #39666073
fkthat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
refregfkthatВо время foreach коллекцию менять нельзя в принципе - это в сам .NET встроено. Будет иксепшен.А если свой Enumerator реализовать?

Если свой, то, как бы, можно. Только при этом можно легко нарваться на всякие неприятные вещи, типа, перечисления одного и того же элемента два раза, или наоборот пропуска какого-нибудь элемента. От этого в стандартные коллекции фреймворка и встроена защита в виде запрета на изменение.
...
Рейтинг: 0 / 0
26.06.2018, 15:44
    #39666090
Petro123
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
fkthat,
Даже не в ошибке дело, а в самом смысле.
Как будем ходить по элементам если спереди могут всё грохнуть, сзади и прямо под ногами другим потоком?
Смысл foreach - обойти список весь, а не топать где ступенька появилась.
Единственный вариант без блока это версионность, но подходит ли по условию это к ТС.
...
Рейтинг: 0 / 0
26.06.2018, 16:32
    #39666124
refreg
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
fkthatrefregпропущено...
А если свой Enumerator реализовать?Если свой, то, как бы, можно. Только при этом можно легко нарваться на всякие неприятные вещи, типа, перечисления одного и того же элемента два раза, или наоборот пропуска какого-нибудь элемента. От этого в стандартные коллекции фреймворка и встроена защита в виде запрета на изменение.То есть ты против тех задания?
...
Рейтинг: 0 / 0
26.06.2018, 16:33
    #39666126
ViPRos
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Petro123fkthat,
Даже не в ошибке дело, а в самом смысле.
Как будем ходить по элементам если спереди могут всё грохнуть, сзади и прямо под ногами другим потоком?
Смысл foreach - обойти список весь, а не топать где ступенька появилась.
Единственный вариант без блока это версионность, но подходит ли по условию это к ТС.
надо CQRS + ES :)
...
Рейтинг: 0 / 0
26.06.2018, 19:00
    #39666190
ВМоисеев
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
>SergASh, вчера, 13:33 http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1296919&msg=21518500] [21518500]

>Имеется последовательность…

int ID можно ли рассматривать в качестве индекса "последовательности (вектора) порядка 10^4"?
...
Рейтинг: 0 / 0
27.06.2018, 09:39
    #39666340
SergASh
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
ВМоисеевint ID можно ли рассматривать в качестве индекса "последовательности (вектора) порядка 10^4"?

По ID элементы последовательности уникальны. Но они идут не подряд, а случайно. Так что выделить память под все возможные айди не получится если в этом был вопрос
...
Рейтинг: 0 / 0
27.06.2018, 09:45
    #39666343
SergASh
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Roman MejtesDima T,

А вообще есть класс SortedSet<T>, в нём сразу реализовано бинарное дерево и всё остальное, можно использовать его. По сути тоже самое. B для вставки, удаления и извлечения объекта там сложно O(log n)

SortedSet<T> отсортирован по одному ключу. Чтоб найти там элемент по другому ключу нужен полный перебор.
...
Рейтинг: 0 / 0
27.06.2018, 09:48
    #39666344
SergASh
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Хитрая очередь с приоритетом без блокировок
Petro123fkthat,
Как будем ходить по элементам если спереди могут всё грохнуть, сзади и прямо под ногами другим потоком?
Смысл foreach - обойти список весь, а не топать где ступенька появилась.
Единственный вариант без блока это версионность, но подходит ли по условию это к ТС.

Эта дилемма понятна. Либо версионность и копирование, либо скорость и хождение по постоянно меняющейся структуре. В этой задаче выбор в пользу последнего.
Если вам известен способ достичь версионности более экономно, чем копированием, то хотелось бы узнать как.
...
Рейтинг: 0 / 0
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Хитрая очередь с приоритетом без блокировок / 25 сообщений из 50, страница 1 из 2
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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