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

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

28.07.2016, 14:35. Показов 23136. Ответов 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
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,576
21.02.2017, 10:44
Студворк — интернет-сервис помощи студентам
C#
1
2
3
4
public T this[int i]
{
      get{ return _holder[i]; }
}
не работает?
2
8 / 8 / 0
Регистрация: 13.12.2016
Сообщений: 48
21.02.2017, 11:40
Работает!!! Спасибо!!!

Добавлено через 51 минуту
Вообще огонь!!!
Сделал еще под свои потребности что бы в листе хранились только 25 самых последних добавлений.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void Add(T elem)
        {
            _listLock.EnterWriteLock();
            try
            {
                _holder.Add(elem);
                while (_holder.Count > 25)
                    _holder.RemoveAt(0);
            }
            finally
            {
                _listLock.ExitWriteLock();
            }
        }
И прям то что нужно получилось!!! А то неделю мозги компосировал, пытаясь работать со стандартным листами
1
 Аватар для EveKS
601 / 485 / 185
Регистрация: 19.04.2016
Сообщений: 1,885
28.10.2017, 17:53
Kertis138, вот пробую я удалить элемент, а тут ошибка, и что делать? Элемент остался, а код выполняется дальше.
0
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 342
29.10.2017, 10:49  [ТС]
Цитата Сообщение от EveKS Посмотреть сообщение
вот пробую я удалить элемент, а тут ошибка, и что делать? Элемент остался, а код выполняется дальше.
Какой код? Какая ошибка?
Предоставьте пожалуйста больше сведений
0
 Аватар для EveKS
601 / 485 / 185
Регистрация: 19.04.2016
Сообщений: 1,885
29.10.2017, 11:03

Не по теме:

как посты от 21.02.2017, 15:40 попадают в верхний список...
https://www.cyberforum.ru/csharp-beginners/
:facepalm:



Добавлено через 8 минут

Не по теме:

Хотя, думаю сценарий был такой, гуглил я синглтон потокобезопасный, вкладка осталась. Спустя какое-то время, захожу я на форум, открываю новые темы, и отвечаю в старой теме, как в новой :)

0
 Аватар для Svarogich
6 / 6 / 0
Регистрация: 05.11.2011
Сообщений: 26
18.10.2018, 12:54
Типом возвращаемого значения надо сделать T:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    public T this[int i]
        {
            get
            {
                _listLock.EnterReadLock();
                try
                {
                    return _holder[i];
                }
                finally
                {
                    _listLock.ExitReadLock();
                }
            }
        }
Добавлено через 2 часа 40 минут
Вот окончательный класс, который умеет всё, что и обычный List, но при этом потокобезопасен. Протестил, глюков не было. Спасибо за наводку!

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
 
namespace Netix.Common.Threading
{
    public class ConcurrentList<T> : IEnumerable<T>
    {
        protected readonly ReaderWriterLockSlim ListLock = new ReaderWriterLockSlim();
        protected readonly List<T> InnerList = new List<T>();
        private int _count;
 
        public void Add(T elem)
        {
            ListLock.EnterWriteLock();
            try
            {
                InnerList.Add(elem);
                _count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, _count - 1));
            }
            finally
            {
                ListLock.ExitWriteLock();
            }
        }
 
        public void AddRange(IEnumerable<T> collection)
        {
            ListLock.EnterWriteLock();
            try
            {
                InnerList.AddRange(collection);
                _count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, _count - 1));
            }
            finally
            {
                ListLock.ExitWriteLock();
            }
        }
 
        public void Insert(int index, T elem)
        {
            ListLock.EnterWriteLock();
            try
            {
                InnerList.Insert(index, elem);
                _count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
            }
            finally
            {
                ListLock.ExitWriteLock();
            }
        }
 
        public void Remove(T elem)
        {
            ListLock.EnterWriteLock();
            try
            {
                if (InnerList.Contains(elem))
                {
                    int index = InnerList.IndexOf(elem);
                    InnerList.Remove(elem);
                    _count = InnerList.Count;
                    OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
                }
            }
            finally
            {
                ListLock.ExitWriteLock();
            }
        }
       
        public int IndexOf(T elem)
        {
            ListLock.EnterReadLock();
            try
            {
                return InnerList.IndexOf(elem);
            }
            finally
            {
                ListLock.ExitReadLock();
            }
        }
 
        public int Count => _count;
 
        public bool Contains(T elem)
        {
            ListLock.EnterReadLock();
            try
            {
                return InnerList.Contains(elem);
            }
            finally
            {
                ListLock.ExitReadLock();
            }
        }
 
        public void Clear()
        {
            ListLock.EnterWriteLock();
            try
            {
                InnerList.Clear();
                _count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, 0));
            }
            finally
            {
                ListLock.ExitWriteLock();
            }
        }
 
        public T Find(Predicate<T> predic)
        {
            ListLock.EnterReadLock();
            try
            {
                return InnerList.Find(predic);
            }
            finally
            {
                ListLock.ExitReadLock();
            }
        }
 
        public void ForEach(Action<T> action)
        {
            ListLock.EnterReadLock();
            try
            {
                InnerList.ForEach(action);
            }
            finally
            {
                ListLock.ExitReadLock();
            }
        }
 
        public void Sort()
        {
            ListLock.EnterWriteLock();
            try
            {
                InnerList.Sort();
            }
            finally
            {
                ListLock.ExitWriteLock();
            }
        }
 
        public void Sort(IComparer<T> comparer)
        {
            ListLock.EnterWriteLock();
            try
            {
                InnerList.Sort(comparer);
            }
            finally
            {
                ListLock.ExitWriteLock();
            }
        }
 
        public T this[int i]
        {
            get
            {
                ListLock.EnterReadLock();
                try
                {
                    return InnerList[i];
                }
                catch (Exception ex)
                {
                    throw;
                }
                finally
                {
                    ListLock.ExitReadLock();
                }
            }
        }
 
        public IEnumerator<T> GetEnumerator()
        {
            ListLock.EnterReadLock();
            try
            {
                return new Enumerator<T>(this, _count);
            }
            finally
            {
                ListLock.ExitReadLock();
            }
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
 
        public event ListChangedEventHandler ListChanged;
 
        protected virtual void OnListChanged(ListChangedEventArgs e)
        {
            ListChanged?.Invoke(this, e);
        }
    }
 
    internal class Enumerator<inT> : IEnumerator<inT>
    {
        private readonly ConcurrentList<inT> _concurrentList;
        private int _index = -1;
        private readonly int _count;
 
        public Enumerator(ConcurrentList<inT> concurrentList, int count)
        {
            _concurrentList = concurrentList;
            _count = count;
        }
 
        public bool MoveNext()
        {
            if (_index == _count - 1)
            {
                Reset();
                return false;
            }
            _index++;
            return true;
        }
 
        public void Reset()
        {
            _index = -1;
        }
 
        public inT Current => _concurrentList[_index];
 
        object IEnumerator.Current => Current;
 
        public void Dispose()
        {
        }
    }
}
0
 Аватар для Svarogich
6 / 6 / 0
Регистрация: 05.11.2011
Сообщений: 26
19.10.2018, 05:30
Сократил код на 50 строк, сделал 3 статических метода для использования в т.ч. снаружи. Вроде больше ничего не нужно здесь. Неплохой лист получился

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
 
namespace Netix.Common.Threading
{
    public class ConcurrentList<T> : IEnumerable<T>
    {
        protected readonly ReaderWriterLockSlim ListLock = new ReaderWriterLockSlim();
        protected readonly List<T> InnerList = new List<T>();
 
        public void Add(T elem)
        {
            RunWriteLockedAction(ListLock, () =>
            {
                InnerList.Add(elem);
                Count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1));
            });
        }
 
        public void AddRange(IEnumerable<T> collection)
        {
            RunWriteLockedAction(ListLock, () =>
            {
                InnerList.AddRange(collection);
                Count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1));
            });
        }
 
        public void Insert(int index, T elem)
        {
            RunWriteLockedAction(ListLock, () =>
            {
                InnerList.Insert(index, elem);
                Count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
            });
        }
 
        public bool Remove(T elem)
        {
            return RunReadLockedFunction(ListLock, () =>
            {
                int index = InnerList.IndexOf(elem);
                if (index >= 0)
                {
                    InnerList.Remove(elem);
                    Count = InnerList.Count;
                    OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
                    return true;
                }
                return false;
            });
        }
       
        public int IndexOf(T elem)
        {
            return RunReadLockedFunction(ListLock, () => InnerList.IndexOf(elem));
        }
 
        public int Count { get; private set; }
 
        public bool Contains(T elem)
        {
            return RunReadLockedFunction(ListLock, () => InnerList.Contains(elem));
        }
 
        public void Clear()
        {
            RunWriteLockedAction(ListLock, () => 
            {
                InnerList.Clear();
                Count = InnerList.Count;
                OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, 0));
            });
        }
 
        public void ForEach(Action<T> action)
        {
            RunReadLockedAction(ListLock, () => InnerList.ForEach(action));
        }
 
        public void Sort()
        {
            RunWriteLockedAction(ListLock, () => InnerList.Sort());
        }
 
        public void Sort(IComparer<T> comparer)
        {
            RunWriteLockedAction(ListLock, () => InnerList.Sort(comparer));
        }
 
        public T this[int i] => RunReadLockedFunction(ListLock, () => InnerList[i]);
 
        public IEnumerator<T> GetEnumerator()
        {
            return RunReadLockedFunction(ListLock, () => new Enumerator<T>(this, Count));
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
 
        public List<T> ToList()
        {
            return RunReadLockedFunction(ListLock, () => new List<T>(InnerList));
        }
 
        public T[] ToArray()
        {
            return RunReadLockedFunction(ListLock, () => InnerList.ToArray());
        }
 
        public event ListChangedEventHandler ListChanged;
 
        protected virtual void OnListChanged(ListChangedEventArgs e)
        {
            ListChanged?.Invoke(this, e);
        }
 
        public static TReturn RunReadLockedFunction<TReturn>(ReaderWriterLockSlim locker, Func<TReturn> action)
        {
            locker.EnterReadLock();
            try
            {
                return action();
            }
            finally
            {
                locker.ExitReadLock();
            }
        }
 
        public static void RunReadLockedAction(ReaderWriterLockSlim locker, Action action)
        {
            locker.EnterReadLock();
            try
            {
                action();
            }
            finally
            {
                locker.ExitReadLock();
            }
        }
 
        public static void RunWriteLockedAction(ReaderWriterLockSlim locker, Action action)
        {
            locker.EnterWriteLock();
            try
            {
                action();
            }
            finally
            {
                locker.ExitWriteLock();
            }
        }
    }
 
    internal class Enumerator<T> : IEnumerator<T>
    {
        private readonly ConcurrentList<T> _concurrentList;
        private int _index = -1;
        private readonly int _count;
 
        public Enumerator(ConcurrentList<T> concurrentList, int count)
        {
            _concurrentList = concurrentList;
            _count = count;
        }
 
        public bool MoveNext()
        {
            if (_index == _count - 1)
            {
                Reset();
                return false;
            }
            _index++;
            return true;
        }
 
        public void Reset()
        {
            _index = -1;
        }
 
        public T Current => _concurrentList[_index];
 
        object IEnumerator.Current => Current;
 
        public void Dispose()
        {
        }
    }
}
0
Эксперт .NET
 Аватар для Usaga
14370 / 9471 / 1360
Регистрация: 21.01.2016
Сообщений: 35,738
19.10.2018, 06:59
Svarogich, при обращении к каждому методу будет создаваться объект лямбды с замыканием для выполнения в методе-обёртки? Классное решение, производительное.
0
 Аватар для Svarogich
6 / 6 / 0
Регистрация: 05.11.2011
Сообщений: 26
22.10.2018, 13:59
Usaga, Прошу пояснить. Как я понял, косяк в том, что ListLock передается без ref и, т.о., создается новый объект, к первому не привязанный. Ок, передадим через ref, согласен. А с Action что не так? Он содержит ссылки на InnerList и, имхо, ничего там не замыкается. Создается он мгновенно. Или я и тут неправ?
1
Эксперт .NET
 Аватар для Usaga
14370 / 9471 / 1360
Регистрация: 21.01.2016
Сообщений: 35,738
22.10.2018, 14:07
Svarogich, речь о том, что лямбда - класс, объект которого будет создаваться каждый раз перед передачей в RunReadLockedFunction. Все внешние ссылки, что такой класс использует, будут "захвачены" с помощью создания другого класса и передачи его в лямбду.

Т.е. каждый вызов метод "синхронной" коллекции будет приводить к созданию мусора.

Подобные приёмы не критичны в обычных ситуациях, но у вас коллекция, которая потенциально может использоваться в нагруженных сценариях. Получается, что такая коллекция будет сильно мусорить совершенно без причины. Плохое решение.
1
 Аватар для Svarogich
6 / 6 / 0
Регистрация: 05.11.2011
Сообщений: 26
22.10.2018, 14:24
Usaga, Ясно, спасибо. А есть ли хорошее и таки красивое решение, не предполагающее try{}catch в каждом методе?
1
 Аватар для Svarogich
6 / 6 / 0
Регистрация: 05.11.2011
Сообщений: 26
23.10.2018, 08:25
Перестроил лист, вынеся лямбды в два отдельных словарика, чтоб не плодились бесконтрольно. От полутора десятков лишних экшенов сервер не ляжет, полагаю. Конструкция чуток усложнилась, но лист остался расширяемым. Жду критики.

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
 
namespace Netix.Common.Threading
{
    public class ConcurrentList<T> : IEnumerable<T>
    {
        protected readonly ReaderWriterLockSlim ListLocker = new ReaderWriterLockSlim();
        protected readonly List<T> InnerList = new List<T>();
 
        protected Dictionary<string, Action> ActionsDictionary = new Dictionary<string, Action>();
        protected Dictionary<string, Func<object>> FunctionsDictionary = new Dictionary<string, Func<object>>();
        protected object ActionParameter;
        protected int ListIndex;
        protected const string OperatorThis = "OperatorThis";
 
        public void Add(T elem)
        {
            RunWriteLockedAction(nameof(Add), elem);
        }
 
        public void AddRange(IEnumerable<T> collection)
        {
            RunWriteLockedAction(nameof(AddRange), collection);
        }
 
        public void Insert(int index, T elem)
        {
            RunWriteLockedAction(nameof(Insert), elem, index);
        }
 
        public bool Remove(T elem)
        {
            return RunReadLockedFunction<bool>(nameof(Remove), elem);
        }
 
        public int IndexOf(T elem)
        {
            return RunReadLockedFunction<int>(nameof(IndexOf), elem);
        }
 
        public int Count { get; private set; }
 
        public bool Contains(T elem)
        {
            return RunReadLockedFunction<bool>(nameof(Contains), elem);
        }
 
        public void Clear()
        {
            RunWriteLockedAction(nameof(Clear));
        }
 
        public void ForEach(Action<T> action)
        {
            RunReadLockedAction(nameof(ForEach), action);
        }
 
        public void Sort()
        {
            RunWriteLockedAction(nameof(Sort));
        }
 
        public void Sort(IComparer<T> comparer)
        {
            RunWriteLockedAction(nameof(Sort), comparer);
        }
 
        public T this[int i] => RunReadLockedFunction<T>(OperatorThis, i);
 
        public IEnumerator<T> GetEnumerator()
        {
            return RunReadLockedFunction<IEnumerator<T>>(nameof(GetEnumerator));
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
 
        public List<T> ToList()
        {
            return RunReadLockedFunction<List<T>>(nameof(ToList));
        }
 
        public T[] ToArray()
        {
            return RunReadLockedFunction<T[]>(nameof(ToArray));
        }
 
        public event ListChangedEventHandler ListChanged;
 
        protected virtual void OnListChanged(ListChangedEventArgs e)
        {
            ListChanged?.Invoke(this, e);
        }
 
        protected TReturn RunReadLockedFunction<TReturn>(string methodName, object methodParameter = null)
        {
            ListLocker.EnterReadLock();
            try
            {
                ActionParameter = methodParameter;
                return (TReturn) GetFunctionByName(methodName).Invoke();
            }
            finally
            {
                ListLocker.ExitReadLock();
            }
        }
 
        protected void RunReadLockedAction(string methodName, object methodParameter = null, int listIndex = -1)
        {
            ListLocker.EnterReadLock();
            try
            {
                ActionParameter = methodParameter;
                ListIndex = listIndex;
                GetActionByName(methodName).Invoke();
            }
            finally
            {
                ListLocker.ExitReadLock();
            }
        }
 
        protected void RunWriteLockedAction(string methodName, object methodParameter = null, int listIndex = -1)
        {
            ListLocker.EnterWriteLock();
            try
            {
                ActionParameter = methodParameter;
                ListIndex = listIndex;
                GetActionByName(methodName).Invoke();
            }
            finally
            {
                ListLocker.ExitWriteLock();
            }
        }
 
        protected virtual Action GetActionByName(string methodName)
        {
            if (ActionsDictionary.ContainsKey(methodName))
            {
                return ActionsDictionary[methodName];
            }
            switch (methodName)
            {
                case nameof(Add):
                    ActionsDictionary[methodName] = () =>
                    {
                        InnerList.Add((T) ActionParameter);
                        Count = InnerList.Count;
                        OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1));
                    };
                    return ActionsDictionary[methodName];
 
                case nameof(AddRange):
                    ActionsDictionary[methodName] = () =>
                    {
                        InnerList.AddRange((IEnumerable<T>) ActionParameter);
                        Count = InnerList.Count;
                        OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1));
                    };
                    return ActionsDictionary[methodName];
 
                case nameof(Insert):
                    ActionsDictionary[methodName] = () =>
                    {
                        InnerList.Insert(ListIndex, (T) ActionParameter);
                        Count = InnerList.Count;
                        OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, ListIndex));
                    };
                    return ActionsDictionary[methodName];
 
                case nameof(Clear):
                    ActionsDictionary[methodName] = () =>
                    {
                        InnerList.Clear();
                        Count = InnerList.Count;
                        OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, 0));
                    };
                    return ActionsDictionary[methodName];
 
                case nameof(Sort):
                    ActionsDictionary[methodName] = () =>
                    {
                        if (ActionParameter is IComparer<T> comparer)
                        {
                            InnerList.Sort(comparer);
                        }
                        else
                        {
                            InnerList.Sort();
                        }
                        OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, Count - 1));
                    };
                    return ActionsDictionary[methodName];
 
                case nameof(ForEach):
                    ActionsDictionary[methodName] = () =>
                    {
                        InnerList.ForEach((Action<T>) ActionParameter);
                    };
                    return ActionsDictionary[methodName];
            }
            throw new NotImplementedException($"Action for method {methodName} is not defined");
        }
 
        protected virtual Func<object> GetFunctionByName(string methodName)
        {
            if (FunctionsDictionary.ContainsKey(methodName))
            {
                return FunctionsDictionary[methodName];
            }
            switch (methodName)
            {
                case nameof(Remove):
                    FunctionsDictionary[methodName] = () =>
                    {
                        int index = InnerList.IndexOf((T)ActionParameter);
                        if (index >= 0)
                        {
                            InnerList.Remove((T)ActionParameter);
                            Count = InnerList.Count;
                            OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
                            return true;
                        }
                        return false;
                    };
                    return FunctionsDictionary[methodName];
 
                case nameof(IndexOf):
                    FunctionsDictionary[methodName] = () => InnerList.IndexOf((T) ActionParameter);
                    return FunctionsDictionary[methodName];
 
                case nameof(Contains):
                    FunctionsDictionary[methodName] = () => InnerList.Contains((T)ActionParameter);
                    return FunctionsDictionary[methodName];
 
                case nameof(OperatorThis):
                    FunctionsDictionary[methodName] = () => InnerList[(int) ActionParameter];
                    return FunctionsDictionary[methodName];
 
                case nameof(GetEnumerator):
                    FunctionsDictionary[methodName] = () => new Enumerator<T>(this, Count);
                    return FunctionsDictionary[methodName];
 
                case nameof(ToList):
                    FunctionsDictionary[methodName] = () => new List<T>(InnerList);
                    return FunctionsDictionary[methodName];
 
                case nameof(ToArray):
                    FunctionsDictionary[methodName] = () => InnerList.ToArray();
                    return FunctionsDictionary[methodName];
            }
            throw new NotImplementedException($"Function for method {methodName} is not defined");
        }
    }
 
    internal class Enumerator<T> : IEnumerator<T>
    {
        private readonly ConcurrentList<T> _concurrentList;
        private int _index = -1;
        private readonly int _count;
 
        public Enumerator(ConcurrentList<T> concurrentList, int count)
        {
            _concurrentList = concurrentList;
            _count = count;
        }
 
        public bool MoveNext()
        {
            if (_index == _count - 1)
            {
                Reset();
                return false;
            }
            _index++;
            return true;
        }
 
        public void Reset()
        {
            _index = -1;
        }
 
        public T Current => _concurrentList[_index];
 
        object IEnumerator.Current => Current;
 
        public void Dispose()
        {
        }
    }
}
0
Эксперт .NET
 Аватар для Usaga
14370 / 9471 / 1360
Регистрация: 21.01.2016
Сообщений: 35,738
23.10.2018, 16:48
Svarogich, это ещё хуже)

Обратите внимание, что работая с некоторой закрытой (от изменения) реализацией коллекции, сделать работу с ней потокобезопасной каким-то эффективным или элегантным образом не получится.

Если вы работаете с List<T> в качестве основы своей потокобезопасной коллекции, то самым простым и эффективным решением будет использование ReaderWriterLockSlim и try\finally везде. Всё остальное - уже мудрёж.

Если нужно ещё более эффективное или элегантное решение, то вам придётся использовать свою реализацию List<T>.
1
0 / 0 / 1
Регистрация: 06.09.2017
Сообщений: 29
25.02.2020, 11:59
Извиняюсь что поднимаю старую тему. Но вопрос для меня актуальный.
Решил воспользоваться этой коллекцией так как в своем коде использовал Монитор

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
 public SortableBindingList<LogRow> FiltredLogRows
        {
            get
            {
                try
                {
                    Monitor.TryEnter(_FiltredLogRows);  
                        return _FiltredLogRows;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                finally
                {
                    if (Monitor.IsEntered(_FiltredLogRows))
                        Monitor.Exit(_FiltredLogRows);
                }
            }
            set
            {
                try
                {
                    Monitor.TryEnter(_FiltredLogRows);
                    _FiltredLogRows = value;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                finally
                {
                    if(Monitor.IsEntered(_FiltredLogRows))
                        Monitor.Exit(_FiltredLogRows);
                }
            }
        }
но регулярно получаю ошибку что коллекция была изменена.
Видимо придется использовать либо потокозащищенную коллекцию либо попробовать этот лист на основе ReaderWriterLockSlim так как производительность важна.

Правда я его наследовал не от IEnumerable<T> а от BindingList<T>
и не могу корректно его скопировать так как нужно допилить конструктор с передачей другого листа

_FiltredLogRows = new SortableBindingList<LogRow>(_LogRows);

По итогу тут создается просто пустой лист. Я так понимаю нужно допилить конструктор самого листа тут

C#
1
public SortableBindingList(IList<T> list) : base(list){}
пробовал добавить

this.AddRange(list);

не работает.
0
8 / 8 / 0
Регистрация: 13.12.2016
Сообщений: 48
25.02.2020, 12:03
Цитата Сообщение от unq Посмотреть сообщение
Извиняюсь что поднимаю старую тему. Но вопрос для меня актуальный.
Решил воспользоваться этой коллекцией так как в своем коде использовал Монитор

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
 public SortableBindingList<LogRow> FiltredLogRows
        {
            get
            {
                try
                {
                    Monitor.TryEnter(_FiltredLogRows);  
                        return _FiltredLogRows;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                finally
                {
                    if (Monitor.IsEntered(_FiltredLogRows))
                        Monitor.Exit(_FiltredLogRows);
                }
            }
            set
            {
                try
                {
                    Monitor.TryEnter(_FiltredLogRows);
                    _FiltredLogRows = value;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                finally
                {
                    if(Monitor.IsEntered(_FiltredLogRows))
                        Monitor.Exit(_FiltredLogRows);
                }
            }
        }
но регулярно получаю ошибку что коллекция была изменена.
Видимо придется использовать либо потокозащищенную коллекцию либо попробовать этот лист на основе ReaderWriterLockSlim так как производительность важна.

Правда я его наследовал не от IEnumerable<T> а от BindingList<T>
и не могу корректно его скопировать так как нужно допилить конструктор с передачей другого листа

_FiltredLogRows = new SortableBindingList<LogRow>(_LogRows);

По итогу тут создается просто пустой лист. Я так понимаю нужно допилить конструктор самого листа тут

C#
1
public SortableBindingList(IList<T> list) : base(list){}
пробовал добавить

this.AddRange(list);

не работает.
Держи полный код потокобезопасного List
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/// <inheritdoc />
/// <summary>
/// Потокобезопасная коллекция
/// </summary>
/// <typeparam name="T">Тип объектов коллекции</typeparam>
public class SafeList<T> : IList<T>
{
    /// <summary>
    /// Сама коллекция  
    /// </summary>
    private List<T> ConcurrentList { get; }
 
    /// <summary>
    /// Блокиратор
    /// </summary>
    private ReaderWriterLockSlim Locker { get; }
 
    /// <summary>
    /// Конструктор
    /// </summary>
    public SafeList()
    {
        Locker = new ReaderWriterLockSlim();
 
        ConcurrentList = new List<T>();
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Функционал для перебора внутренних объектов в контейнере
    /// </summary>
    /// <returns></returns>
    public IEnumerator<T> GetEnumerator() => Clone().GetEnumerator();
 
    /// <inheritdoc />
    /// <summary>
    /// Функционал для перебора внутренних объектов в контейнере
    /// </summary>
    /// <returns></returns>
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
    /// <inheritdoc />
    /// <summary>
    /// Добавление объекта в коллекцию
    /// </summary>
    /// <param name="item">Объект</param>
    public void Add(T item)
    {
        Locker.EnterWriteLock();
 
        try
        {
            ConcurrentList.Add(item);
        }
        finally
        {
            Locker.ExitWriteLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Очистка коллекции
    /// </summary>
    public void Clear()
    {
        Locker.EnterWriteLock();
 
        try
        {
            ConcurrentList.Clear();
        }
        finally
        {
            Locker.ExitWriteLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Проверка содержания объекта в коллекции
    /// </summary>
    /// <param name="item">Объект</param>
    /// <returns>true - содержит</returns>
    /// <returns>false - не содержит</returns>
    public bool Contains(T item)
    {
        Locker.EnterReadLock();
 
        try
        {
            return ConcurrentList.Contains(item);
        }
        finally
        {
            Locker.ExitReadLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Копирование в указанное место
    /// </summary>
    /// <param name="array"></param>
    /// <param name="arrayIndex"></param>
    public void CopyTo(T[] array, int arrayIndex)
    {
        Locker.EnterWriteLock();
 
        try
        {
            ConcurrentList.CopyTo(array, arrayIndex);
        }
        finally
        {
            Locker.ExitWriteLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Удаление объекта из коллекции
    /// </summary>
    /// <param name="item">Объект</param>
    /// <returns>true - объект удален</returns>
    /// <returns>false - объект не удален</returns>
    public bool Remove(T item)
    {
        Locker.EnterWriteLock();
 
        try
        {
            return ConcurrentList.Remove(item);
        }
        finally
        {
            Locker.ExitWriteLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Количество объектов в коллекции
    /// </summary>
    public int Count
    {
        get
        {
            Locker.EnterReadLock();
 
            try
            {
                return ConcurrentList.Count;
            }
            finally
            {
                Locker.ExitReadLock();
            }
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Только ли для чтения коллекция
    /// </summary>
    public bool IsReadOnly => false;
 
    /// <inheritdoc />
    /// <summary>
    /// Получить индекс объекта в коллекции
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public int IndexOf(T item)
    {
        Locker.EnterReadLock();
 
        try
        {
            return ConcurrentList.IndexOf(item);
        }
        finally
        {
            Locker.ExitReadLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Вставить объект по индексу
    /// </summary>
    /// <param name="index"></param>
    /// <param name="item"></param>
    public void Insert(int index, T item)
    {
        Locker.EnterWriteLock();
 
        try
        {
            ConcurrentList.Insert(index, item);
        }
        finally
        {
            Locker.ExitWriteLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Удалить из коллекции по индексу
    /// </summary>
    /// <param name="index"></param>
    public void RemoveAt(int index)
    {
        Locker.EnterWriteLock();
 
        try
        {
            ConcurrentList.RemoveAt(index);
        }
        finally
        {
            Locker.ExitWriteLock();
        }
    }
 
    /// <inheritdoc />
    /// <summary>
    /// Взять или присвоить объект по индексу в коллекции
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public T this[int index]
    {
        get
        {
            Locker.EnterReadLock();
 
            try
            {
                return ConcurrentList[index];
            }
            finally
            {
                Locker.ExitReadLock();
            }
        }
        set
        {
            Locker.EnterWriteLock();
 
            try
            {
                ConcurrentList[index] = value;
            }
            finally
            {
                Locker.ExitWriteLock();
            }
        }
    }
 
    /// <summary>
    /// Клонирование коллекции
    /// </summary>
    /// <returns></returns>
    private List<T> Clone()
    {
        var newList = new List<T>();
 
        Locker.EnterReadLock();
 
        try
        {
            ConcurrentList.ForEach(x => newList.Add(x));
 
        }
        finally
        {
            Locker.ExitReadLock();
        }
 
        return newList;
    }
}
2
0 / 0 / 1
Регистрация: 06.09.2017
Сообщений: 29
25.02.2020, 12:13
Цитата Сообщение от spocher Посмотреть сообщение
Держи полный код потокобезопасного List
Оу, круть спасибо. А получится его прикрутить к биндинглисту?

у меня биндинг еще с допиленной сортировкой

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
 public class SortableBindingList<T> : BindingList<T>
    {
        private bool isSortedValue;
        ListSortDirection sortDirectionValue;
        PropertyDescriptor sortPropertyValue;
        public SortableBindingList() : base() { }
        public SortableBindingList(IList<T> list) : base(list) { }
 
        protected override void ApplySortCore(PropertyDescriptor prop,
            ListSortDirection direction)
        {
            Type interfaceType = prop.PropertyType.GetInterface("IComparable");
            if (interfaceType == null && prop.PropertyType.IsValueType)
            {
                Type underlyingType = Nullable.GetUnderlyingType(prop.PropertyType);
                if (underlyingType != null)
                {
                    interfaceType = underlyingType.GetInterface("IComparable");
                }
            }
            if (interfaceType != null)
            {
                sortPropertyValue = prop;
                sortDirectionValue = direction;
                IEnumerable<T> query = base.Items;
                query = direction == ListSortDirection.Ascending ? query.OrderBy(i => prop.GetValue(i)) : query.OrderByDescending(i => prop.GetValue(i));
                var newIndex = 0;
                foreach (object item in query)
                {
                    this.Items[newIndex] = (T)item;
                    newIndex++;
                }
                isSortedValue = true;
                sorting = true;
                this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
                sorting = false;
            }
            else
            {
                throw new NotSupportedException("Cannot sort by " + prop.Name +
                    ". This" + prop.PropertyType.ToString() +
                    " does not implement IComparable");
            }
        }
        bool sorting = false;
 
        protected override PropertyDescriptor SortPropertyCore => sortPropertyValue;
 
        protected override ListSortDirection SortDirectionCore => sortDirectionValue;
 
        protected override bool SupportsSortingCore => true;
 
        protected override bool IsSortedCore => isSortedValue;
 
        protected override void RemoveSortCore()
        {
            isSortedValue = false;
            sortPropertyValue = null;
        }
        protected override void OnListChanged(ListChangedEventArgs e)
        {
            if (!sorting && sortPropertyValue != null)
                ApplySortCore(sortPropertyValue, sortDirectionValue);
            else
                base.OnListChanged(e);
        }
}
0
0 / 0 / 1
Регистрация: 06.09.2017
Сообщений: 29
26.02.2020, 21:53
запилил в биндинглист все схавал за исключение

C#
1
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
получаю

вмещающий тип не реализует интерфейс "IEnumerable".

как избавиться?

Добавлено через 34 минуты
Почитал всякие форумы и как я понимаю не получится создать потокозащищенную коллекцию на основе BindingList<T>
так как начинаются проблемы при связывании его в качестве DataSource с элементами управления на форме.

Все выше предложенные классы создают узкий потокозащищенный функционал с поиском по индексу удалению и добавлению элемента. Что же касается DataSource то этого функционала нет и не прикрутишь((((
0
0 / 0 / 0
Регистрация: 16.07.2020
Сообщений: 1
16.07.2020, 14:15
Цитата Сообщение от spocher Посмотреть сообщение
Держи полный код потокобезопасного List
Глупый вопрос: а почему в методе CopyTo ты используешь блокировку EnterWriteLock, а не EnterReadLock? Ведь по идее, из списка ConcurrentList будет осуществляться только чтение...
0
8 / 8 / 0
Регистрация: 13.12.2016
Сообщений: 48
16.07.2020, 14:19
Цитата Сообщение от tms320 Посмотреть сообщение
Глупый вопрос: а почему в методе CopyTo ты используешь блокировку EnterWriteLock, а не EnterReadLock? Ведь по идее, из списка ConcurrentList будет осуществляться только чтение...
Потому что в конечную коллекцию идет запись по индексу
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
16.07.2020, 14:19

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

Конвертировать один элемент из 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;, в...


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

Или воспользуйтесь поиском по форуму:
39
Ответ Создать тему
Новые блоги и статьи
Нейтральные знания ..., ... чистая наука. Пока что-то проходит модерацию на Хабре, стоит развить мысль ...
Hrethgir 20.07.2026
К таким радикальным взглядам я конечно в той публикации не приходил, но чтобы скоротать вечер, решил углубиться немного. 1. Почему показания термометра заряжены целью? Цель заложена в самом. . .
Установка нескольких штампов электронной подписи в строго определенных местах файла docx
ВладимирСамохин 19.07.2026
(В!) Работа с Электронной подписью - это неотъемлемая часть современного документооборота. Но что делать, если нужно поставить несколько штампов электронной подписи в строго определенных местах. . .
сукцессия 35. Научная статья о проделанной работе
anaschu 19.07.2026
Написал в формате латекс и пдф
Вангую, что это не пройдёт модерацию, и на неделе я запущу свой сервер.
Hrethgir 19.07.2026
Эта публикация сейчас в песочнице и ждёт приглашения. https:/ / habr. com/ ru/ sandbox/ 295048/ По ссылке 403. Не очень информативно такую ссылку постить. Запись от Usaga размещена Сегодня в 06:46 . . .
сукцессия 33. открытые вопросы от клауде
anaschu 19.07.2026
"Что накопилось за эту часть А — тринадцать правок, из которых шесть пришли из ваших вопросов и каждая оказалась реальной ошибкой, а не калибровкой: односторонний симбиоз, отсутствующий листопад,. . .
32 сукцессия
anaschu 19.07.2026
сукцессия 28‑мерное ядро стабилизировано Коллеги, фиксирую разбор инженерных правок и их изоморфную проекцию на экономику, меметику и половой отбор. Модель теперь не «подкручивает» сходимость —. . .
сукцессия 31: модель микоризы - это модель ещё нескольких явлений, социальных и экономических
anaschu 18.07.2026
Теория «Всего»: апдейт v1. 1. 2 — 28‑мерное ядро стабилизировано Коллеги, фиксирую разбор инженерных правок и их изоморфную проекцию на экономику, меметику и половой отбор. Модель теперь не. . .
сукцессия 30. Массив проверяющих друг друга моделей
anaschu 18.07.2026
Архитектура сети взаимопроверяющих моделей микоризной сукцессии (v2. 0) Развитие тензорного ОДУ-ядра и создание кросс-платформенного калибровочного полигона Уважаемые коллеги! В продолжение. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru