Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.68/107: Рейтинг темы: голосов - 107, средняя оценка - 4.68
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342

Потокобезопасный List

28.07.2016, 14:35. Показов 22828. Ответов 38
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!

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

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
    internal class ConcurrentList<T>
    {
        private readonly List<T> _holder = new List<T>();
 
        public void Add(T elem)
        {
            lock (_holder)
            {
                _holder.Add(elem);
            }
        }
 
        public T ElementAt(int ind)
        {
            lock (_holder)
            {
               return _holder.ElementAt(ind);
            }
        }
 
        public int IndexOf(T elem)
        {
            lock (_holder)
            {
                return _holder.IndexOf(elem);
            }
        }
    }
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
28.07.2016, 14:35
Ответы с готовыми решениями:

Потокобезопасный метод
Обозначу суть проблемы. Есть кнопка, по нажатию которой, вызывается метод, работающий с элементами формы в другом потоке. Т.к. обращений к...

Потокобезопасный массив
Есть decimal ararB1B2. Несколько Task, работая параллельно: должны складывать разные decimal с и результат помещать в по тому же адресу...

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

38
28.07.2016, 14:42

Не по теме:

Есть стандартные списки потокобезопасные. Доступ по индексу посчитали не нужным. Может он и Вам на самом деле не нужен?)

0
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 14:44  [ТС]
Rius, Да, не нужен, я просто написал в качестве примера. Мне нужен метод Remove(T elem) и поиск элемента, поле которого удовлетворяет условиям (Тут же без доступа по индексу можно?)
А остальное корректно написано? Все с потокобезопастностью хорошо?
0
Эксперт .NET
 Аватар для Rius
13102 / 7660 / 1673
Регистрация: 25.05.2015
Сообщений: 23,353
Записей в блоге: 14
28.07.2016, 14:52
Нормально.
Посмотрите существующие средства https://msdn.microsoft.com/en-... 97305.aspx
1
Эксперт .NET
 Аватар для Usaga
14138 / 9366 / 1350
Регистрация: 21.01.2016
Сообщений: 35,234
28.07.2016, 15:09
Kertis138, не смотря на то, что лучшим выходом будет поискать готовое решение (как уже было предложено выше), не следует забыть о такой классной штуке как ReaderWriterLockSlim. Этот механизм синхронизации умеет блокировать потоки только тогда, когда один из них пытается выполнить запись, параллельному чтению данный механизм не препятствует. В этом его огромное преимущество перед lock. В примере в первом посте такая штука могла бы быть более уместна, чем lock.
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 15:18  [ТС]
Usaga,
Ну получилось что-то вот такое:
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
    internal class ConcurrentList<T>
    {
        private readonly ReaderWriterLockSlim _listLock = new ReaderWriterLockSlim();
        private readonly List<T> _holder = new List<T>();
 
        public void Add(T elem)
        {
            _listLock.EnterWriteLock();
            try
            {
                _holder.Add(elem);
            }
            finally
            {
                _listLock.ExitWriteLock();
            }
        }
 
        public int IndexOf(T elem)
        {
            _listLock.EnterReadLock();
            try
            {
                return _holder.IndexOf(elem);
            }
            finally
            {
                _listLock.ExitReadLock();
            }
        }
    }
Список сам полностью обеспечивает потокобезопастность? Теперь из вне я могу им пользоваться из разных потоков?

Добавлено через 1 минуту
Цитата Сообщение от Rius Посмотреть сообщение
Посмотрите существующие средства https://msdn.microsoft.com/en-... 97305.aspx
Я пробовал ConcurrentBag<T>, но у меня с ним не получилось доставать и удалять элементы. Не нашел нормального примера использования
0
Эксперт .NET
 Аватар для Usaga
14138 / 9366 / 1350
Регистрация: 21.01.2016
Сообщений: 35,234
28.07.2016, 15:23
Цитата Сообщение от Kertis138 Посмотреть сообщение
Список сам полностью обеспечивает потокобезопастность?
Список обеспечивает только хранение элементов. Потокобезопасность ты сам обеспечиваешь.

Кстати, такие вещи как IndexOf очень опасны в многопоточном приложении. Не рекомендую использовать. Между поиском индекса элемента и его запросом (через другой метод, как в примере в первом посте) содержимое списка может сильно поменять другим потоком и полученный ранее индекс может стать неактуальным. Поиск и получение объекта нужно выполнять как одну атомарную процедуру.

Цитата Сообщение от Kertis138 Посмотреть сообщение
Я пробовал ConcurrentBag<T>, но у меня с ним не получилось доставать и удалять элементы.
Значит ты что-то сделал не так.
1
9 / 9 / 6
Регистрация: 13.11.2012
Сообщений: 173
28.07.2016, 15:28
Kertis138, а очередь не подойдет для Вашей задачи?
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 15:29  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
Значит ты что-то сделал не так.
Видимо, что-то не так.
Там есть метод TryTake и она возвращает bool.
Как мне удалить элемент из коллекции и что делать с этим bool ?
0
Эксперт .NET
 Аватар для Usaga
14138 / 9366 / 1350
Регистрация: 21.01.2016
Сообщений: 35,234
28.07.2016, 15:33
Kertis138, TryTake возвращает полученный объект через аргумент переданный с модификатором OUT. Bool - признак того, получилось ли вытащить из коллекции объект или нет.

Я не знаю, где ты пример смотрел, но вот этот (с MSDN) вполне себе понятен:
Кликните здесь для просмотра всего текста

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
using System;
using System.Collections.Concurrent;
 
class ConcurrentBagDemo
{
    // Demonstrates:
    //      ConcurrentBag<T>.Add()
    //      ConcurrentBag<T>.IsEmpty
    //      ConcurrentBag<T>.TryTake()
    //      ConcurrentBag<T>.TryPeek()
    static void Main()
    {
        // Construct and populate the ConcurrentBag
        ConcurrentBag<int> cb = new ConcurrentBag<int>();
        cb.Add(1);
        cb.Add(2);
        cb.Add(3);
 
        // Consume the items in the bag
        int item;
        while (!cb.IsEmpty)
        {
            if (cb.TryTake(out item))
                Console.WriteLine(item);
            else
                Console.WriteLine("TryTake failed for non-empty bag");
        }
 
        // Bag should be empty at this point
        if (cb.TryPeek(out item))
            Console.WriteLine("TryPeek succeeded for empty bag!");
    }
}
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 15:36  [ТС]
Usaga, Ну вот я попытаюсь удалить элемент, а удаление будет не успешным. Но мне то нужно как-то удалить элемент. С помощью while пытаться удалить, пока не удалит?
0
Эксперт .NET
 Аватар для Usaga
14138 / 9366 / 1350
Регистрация: 21.01.2016
Сообщений: 35,234
28.07.2016, 15:43
Kertis138, удаление будет неуспешным только в том случае, если коллекция пуста (в ней нечего удалять - или не положили, или другой поток уже забрал последний элемент).
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 15:44  [ТС]
Цитата Сообщение от Garcian Посмотреть сообщение
а очередь не подойдет для Вашей задачи?
Мне нужен доступ к любому элементу, а не только первому
0
Эксперт .NET
 Аватар для Usaga
14138 / 9366 / 1350
Регистрация: 21.01.2016
Сообщений: 35,234
28.07.2016, 15:48
Цитата Сообщение от Kertis138 Посмотреть сообщение
Мне нужен доступ к любому элементу, а не только первому
Тогда ConcurrentBag<T> тебе не подойдёт - это неупорядоченная коллекция без потокобезопасного способа навигации по содержимому. Итератор, который можно получить из этой коллекции представляет из себя "снапшот" содержимого коллекции на момент запроса и не отражает изменений произошедших после.
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 15:57  [ТС]
Usaga, Ну тогда буду обертку использовать.
Она же у меня корректно написана?

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
    internal class ConcurrentList<T>
    {
        private readonly ReaderWriterLockSlim _listLock = new ReaderWriterLockSlim();
        private readonly List<T> _holder = new List<T>();
 
        public void Add(T elem)
        {
            _listLock.EnterWriteLock();
            try
            {
                _holder.Add(elem);
            }
            finally
            {
                _listLock.ExitWriteLock();
            }
        }
 
        public void Remove(T elem)
        {
            _listLock.EnterWriteLock();
            try
            {
                _holder.Remove(elem);
            }
            finally
            {
                _listLock.ExitWriteLock();
            }
        }
    }
0
Эксперт .NET
 Аватар для Usaga
14138 / 9366 / 1350
Регистрация: 21.01.2016
Сообщений: 35,234
28.07.2016, 16:12
Лучший ответ Сообщение было отмечено Kertis138 как решение

Решение

Kertis138, не вижу у тебя методов доступа к хранимым объектам, а без этого от такой коллекции мало толку К тому же в методе Remove не производится проверка на наличие объекта в коллекции - может его только что другой поток удалил?
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 16:36  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
не вижу у тебя методов доступа к хранимым объектам, а без этого от такой коллекции мало толку К тому же в методе Remove не производится проверка на наличие объекта в коллекции - может его только что другой поток удалил?
Исправляюсь:

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
122
      internal class ConcurrentList<T> : IEnumerable<T>
    {
        private readonly ReaderWriterLockSlim _listLock = new ReaderWriterLockSlim();
        private readonly List<T> _holder = new List<T>();
 
        public void Add(T elem)
        {
            _listLock.EnterWriteLock();
            try
            {
                _holder.Add(elem);
            }
            finally
            {
                _listLock.ExitWriteLock();
            }
        }
 
        public void Remove(T elem)
        {
            _listLock.EnterWriteLock();
            try
            {
                if(_holder.Contains(elem))
                    _holder.Remove(elem);
            }
            finally
            {
                _listLock.ExitWriteLock();
            }
        }
 
        public int Count()
        {
            _listLock.EnterReadLock();
            try
            {
                return _holder.Count;
            }
            finally
            {
                _listLock.ExitReadLock();
            }
        }
 
        public void Clear()
        {
            _listLock.EnterWriteLock();
            try
            {
                _holder.Clear();
            }
            finally
            {
                _listLock.ExitWriteLock();
            }
        }
 
        public T First()
        {
            _listLock.EnterReadLock();
            try
            {
                return _holder.First();
            }
            finally
            {
                _listLock.ExitReadLock();
            }
        }
 
        public T Last()
        {
            _listLock.EnterReadLock();
            try
            {
                return _holder.Last();
            }
            finally
            {
                _listLock.ExitReadLock();
            }
        }
 
        public T Find(Predicate<T> predic)
        {
            _listLock.EnterReadLock();
            try
            {
               return _holder.Find(predic);
            }
            finally
            {
                _listLock.ExitReadLock();
            }
        }
 
        public void ForEach(Action<T> action)
        {
            _listLock.EnterWriteLock();
            _listLock.EnterReadLock();
            try
            {
                _holder.ForEach(action);
            }
            finally
            {
                _listLock.ExitWriteLock();
                _listLock.ExitReadLock();
            }
        }
 
        public IEnumerator<T> GetEnumerator()
        {
            return _holder.GetEnumerator();
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
Единственное - ForEach может же как читать, так и писать. Его блокировать по двум фронтам?
GetEnumerator не требует блокировок?
0
Эксперт .NET
 Аватар для Usaga
14138 / 9366 / 1350
Регистрация: 21.01.2016
Сообщений: 35,234
28.07.2016, 17:14
Лучший ответ Сообщение было отмечено Kertis138 как решение

Решение

Kertis138, для ForEach достаточно блокировки на чтение - при ForEach элементы не могут быть удалены. Одновременно использовать EnterWriteLock и EnterReadLock - не нужно. Как я уже писал выше - итератор (Enumerator) не отслеживает состояние родительской коллекции после своего создания и может вернуть (при перечислении) объекты, которых там уже нет, так использовать их стоит с опаской.

Добавлено через 3 минуты
Кстати, что First(), что Last() кинет исключение, если коллекция пуста. Используй FirstOrDefault() и LastOrDefault().

Добавлено через 6 минут
Про итераторы я прогнал - сказанное относится к потокобезопасной коллекции обсуждавшейся ранее. Не используй итераторы списка вне твоей обертки!
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
28.07.2016, 17:30  [ТС]
Usaga, Спасибо за помощь!
0
8 / 8 / 0
Регистрация: 13.12.2016
Сообщений: 48
21.02.2017, 10:34
Приветствую!!!
Очень пригодился ваш код.

Добавил в него возможность обращение к элементу по индексу
C#
1
2
3
ConcurrentList<double> example = new ConcurentList<double>()
example.Add(1.23);
Console.Writeline(example[0]);
Добавил в ваш код:
C#
1
2
3
4
public object this[int i]
{
      get{ return _holder[i]; }
}
И все вроде гуд, но когда лист пользуешь в цикле, теряется тип данных объекта с индексом

Приходится заново приводить к нужному типу элемент листа с индексом.
C#
1
2
3
4
5
6
7
8
9
10
ConcurrentList<double> example = new ConcurentList<double>()
example.Add(1.23);
example.Add(5.66);
example.Add(4.32);
 
for(int i=1; i<Example.Count();i++)
{
      if((double)example[i]<(double)examplr[i-1])
      //.......
}
Поправьте пожалуйста как сделать что бы лист по индексу отдавался сразу с типом данных?
Пытаюсь использовать GetType(), но не знаю в каком месте его использовать
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
21.02.2017, 10:34
Помогаю со студенческими работами здесь

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

Конвертировать один элемент из List<List<Class>> в list и string
Как можно перевести один выбранный элемент из List&lt;List&lt;Data&gt;&gt; myList в list и string? Например, myList. Сейчас я могу с помощью...

Linq преобразование List<List<double>> в List<Array>
Доброго времени суток, данный код нужно преобразовать linq выражениями и дописать выборку из List&lt;List&lt;double&gt;&gt;. Таким...

Необходимо создать dll в которой буду обрабатывать list<list<string>>
Необходимо создать dll в которой буду обрабатывать list&lt;list&lt;string&gt;&gt; как объявить методы класса чтобы я задавал на вход экземпляр...

List<string> или все же List<StringBuilder>, что лучше использовать?
Добрый день, хочется узнать с точки зрения производительности что подходит лучше в данном случае. Есть List&lt;List&lt;T&gt;&gt;, в...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru