82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
1

Заполнение коллекции из разных потоков

24.09.2019, 15:52. Показов 11908. Ответов 23
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день.
Что я могу использовать вместо удобной ObservableCollection<T>, если мне надо добавлять данные из разных потоков?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.09.2019, 15:52
Ответы с готовыми решениями:

Обезопасить доступ к коллекции из разных потоков
станет ли безопасным метод AddSafe? public static MyCollection myCol = new MyCollection(); ...

Заполнение TreeView из разных потоков
Всем здравствуйте. Есть программа которая при запуске заполняет treeView. Почитал и получилось...

Одновременный доступ к коллекции из двух потоков
Здравствуйте. В программе открываю UDP-сокет, в который приходят данные с устройства. Вкратце, у...

Заполнение коллекции из БД
Помогите,пожалуйста, написать метод для выполнения запроса из БД и записи в коллекцию. Нужно взять...

23
Эксперт .NET
1829 / 1337 / 427
Регистрация: 10.06.2011
Сообщений: 2,124
24.09.2019, 18:12 2
Можно оборачивать критическую секцию оператором lock. Такой вариант почему вам не подходит?
C#
1
2
3
4
5
6
7
8
9
private readonly _collectionSync = new object();
 
private void DoSomething()
{
    lock (_collectionSync)
    {
        _observableCollection.Add(new Item());
    }
}
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
24.09.2019, 19:00  [ТС] 3
Цитата Сообщение от novikov.ea Посмотреть сообщение
Такой вариант почему вам не подходит?
Я вообще никаких вариантов не знаю =(
Подходит, почему нет.
А можно побольше кода, а то не очень понятно по обрывку.

Я решил использовать BlockingCollection<T> вместо ObservableCollection<T>, это плохое решение?
0
Эксперт .NET
1829 / 1337 / 427
Регистрация: 10.06.2011
Сообщений: 2,124
24.09.2019, 19:07 4
BlockingCollection не реализует INotifyColletionChanged (ссыль)
Напишите, в чём именно проблема, тогда будет понятно ЧТО нужно решить.
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
24.09.2019, 19:27  [ТС] 5
Цитата Сообщение от novikov.ea Посмотреть сообщение
Напишите, в чём именно проблема
Есть у меня форма, там есть DataGrid который привязан к ObservableCollection<T>
Все было хорошо, но случилось так, что ObservableCollection<T> надо заполнять с двух потоков, а она не заполняется, вот я и думаю, что делать.
Цитата Сообщение от novikov.ea Посмотреть сообщение
BlockingCollection не реализует INotifyColletionChanged
А её никак нельзя обернуть в INotifyColletionChanged?

Может мне лучше пример кода написать, чтобы видно было чего да как?
0
Модератор
Эксперт .NET
13798 / 10005 / 2666
Регистрация: 21.04.2018
Сообщений: 29,798
Записей в блоге: 2
24.09.2019, 19:53 6
Цитата Сообщение от Чипс Посмотреть сообщение
Есть у меня форма, там есть DataGrid который привязан к ObservableCollection<T>
Все было хорошо, но случилось так, что ObservableCollection<T> надо заполнять с двух потоков, а она не заполняется, вот я и думаю, что делать.
ObservableCollection<T> это коллекция с реализацией INСC. Из Net коллекций, по-моему, она единственная. Она сама и, конечно, её наследники.
Проблема использования ObservableCollection<T> в том, что INCC в WPF не приводится к основному потоку и при её обновлении из другого потока будет исключение.

Поэтому использовать ObservableCollection<T> (которая непосредственно привязана в WPF View) в Модели многопоточного приложения нельзя.
Модель (или Модели) должны создать у себя обычную коллекцию. Если одна для нескольких потоков, то с защитой от конфликтов. А при внесении изменений в эту коллекцию сообщать через события в VM о том какие изменения произошли.
Передача данных в VM происходит в DTO типах в самой простой реализации без интерфейса INPC.

VM ловит событие и данные в DTO типах, конвертирует их в типы для отображения (уже с INPC) и в ОСНОВНОМ потоке вносит нужные изменения в коллекцию типа ObservableCollection<T>.

Чипс, сожалею что так сложно. Но проще - чревато исключениями, багами и т.д.
1
Эксперт .NET
1829 / 1337 / 427
Регистрация: 10.06.2011
Сообщений: 2,124
24.09.2019, 20:10 7
Цитата Сообщение от Чипс Посмотреть сообщение
надо заполнять с двух потоков
Если код выполняется не в UI потоке, то тогда нужно добавление элементов в коллекцию сделать в UI потоке
Пример с испоьлзованием ReactiveUI:
C#
1
2
3
4
RxApp.MainThreadScheduler.Shedule(() =>
{
    MyCollection.Add(item);
})
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
25.09.2019, 12:16  [ТС] 8
Млин фигня какая-то!
Теперь из разных потоков нормально добавляется в ObservableCollection<T> или я не из разных потоков добавляю?

Program

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    class Program
    {
        static void Main(string[] args)
        {
            MainWindowVM MainWindowVM = new MainWindowVM();
            MainWindowVM.Start();
 
            Thread.Sleep(5000);
 
            //Delay
            Console.WriteLine(new string('-', 50));
            Console.WriteLine("Ready!");
            Console.ReadKey();
        }
    }


Number

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Number
    {
        public int Value { get; set; }
        public int ProcesName { get; set; }
 
        public Number()
        {
            Value = 1;
        }
 
        public void Proces()
        {
            for(int i = 1; i<= 5; i++)
            {
                Value++;
                //Thread.Sleep(500); //Чтобы видно было, что работают потоки.
                ValueIsChanged?.Invoke(this);
                //Console.WriteLine("Hello");
            }
        }
 
        public event Action<Number> ValueIsChanged;
    }


MainWindowVM

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
public class MainWindowVM
    {
        public ObservableCollection<Number> Numbers { get; set; }
 
        private Thread ProcesThread1;
        private Thread ProcesThread2;
        Number number1;
        Number number2;
 
        public MainWindowVM()
        {
            Numbers = new ObservableCollection<Number>();
            number1 = new Number();
            number2 = new Number();
 
            number1.ValueIsChanged += EventHandlerValueIsChanged;
            number2.ValueIsChanged += EventHandlerValueIsChanged;
 
        }
 
        public void EventHandlerValueIsChanged(Number Number)
        {
            Console.WriteLine("Hello");
            Numbers.Add(new Number() {Value = Number.Value });
        }
 
        public void Start()
        {
            ProcesThread1 = new Thread(number1.Proces);
            ProcesThread1.Start();
 
            ProcesThread2 = new Thread(number2.Proces);
            ProcesThread2.Start();
        }
    }


Добавлено через 1 минуту
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Чипс, сожалею что так сложно.
Нормально, я Вас понял.

Добавлено через 1 минуту
или нельзя с разных потоков наполнять коллекцию которая
Цитата Сообщение от Элд Хасп Посмотреть сообщение
(которая непосредственно привязана в WPF View
???
А в не привязанную набивай сколько хочешь?
0
Модератор
Эксперт .NET
13798 / 10005 / 2666
Регистрация: 21.04.2018
Сообщений: 29,798
Записей в блоге: 2
25.09.2019, 12:47 9
Цитата Сообщение от Чипс Посмотреть сообщение
А в не привязанную набивай сколько хочешь?
Да.
Исключение возникает когда WPF элемент ловит INCC из другого потока.
Если привязки в View к коллекции нет, то заполняется из другого потока без проблем.
Если из несколькими потоков, то может произойти наложение элементов, то есть фактически какие-то элементы могут пропасть. Но исключений не будет.
Цитата Сообщение от Чипс Посмотреть сообщение
Теперь из разных потоков нормально добавляется в ObservableCollection<T> или я не из разных потоков добавляю?
Вроде, из разных.
Но у вас же нет привязки коллекции к WPF-элементу.
Поэтому исключения не возникаю.
Вероятность наложения элементов тоже мизерна. Создаётся всего 10 элементов. Пока запустится второй поток - первый уже отработает.
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
25.09.2019, 13:24  [ТС] 10
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Пока запустится второй поток - первый уже отработает.
Там снять комментарий с задержки можно, я пробовал, всё Ок.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Но у вас же нет привязки коллекции к WPF-элементу.
Поэтому исключения не возникаю.
Хорошо, собственно вопрос в том, что делать если я всё таки захочу сделать эту привязку!
Чем заменить ObservableCollection<T> или её можно как-то адаптировать под наполнение с разных потоков?
0
Модератор
Эксперт .NET
13798 / 10005 / 2666
Регистрация: 21.04.2018
Сообщений: 29,798
Записей в блоге: 2
25.09.2019, 13:33 11
Цитата Сообщение от Чипс Посмотреть сообщение
Там снять комментарий с задержки можно, я пробовал, всё Ок.
Задержка роли не играет.
Создание и запуск нового потока - это миллисекунды.
А заполнение коллекции пятью элементами - сотые или тысячные доли миллисекунды.

Добавлено через 6 минут
Цитата Сообщение от Чипс Посмотреть сообщение
Хорошо, собственно вопрос в том, что делать если я всё таки захочу сделать эту привязку!
Чем заменить ObservableCollection<T> или её можно как-то адаптировать под наполнения с разных потоков?
Я уже написал выше.

Напрямую прокидывать INCC из Model в WPА View нельзя.
Заменить её не чем.
По крайней мере из дефолтных.

В принципе, сторона ViewModel обращённая к View должна работать только в основном потоке.
Сторона обращённая к Model работает в потоках Модели.
А внутри себя VM приводит все изменения из разных потоков к основному потоку.
0
Модератор
Эксперт .NET
13798 / 10005 / 2666
Регистрация: 21.04.2018
Сообщений: 29,798
Записей в блоге: 2
25.09.2019, 13:37 12
Цитата Сообщение от Чипс Посмотреть сообщение
Чем заменить ObservableCollection<T> или её можно как-то адаптировать под наполнение с разных потоков?
Адаптировать можно. Создать наследника и перенаправлять события INCC в основной поток.
Но такая реализация не думаю что будет правильной.
Этот механизм должен быть снаружи коллекции, так как изменение потоков это не функционал коллекции, а функционал ViewModel.
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
25.09.2019, 13:46  [ТС] 13
Да, подключил ObservableCollection<T> к View и всё сразу рухнуло. (см. рис 1).

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Заменить её не чем.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Адаптировать можно. Создать наследника и перенаправлять события INCC в основной поток.
Но такая реализация не думаю что будет правильной.
Мля это полный П...

Цитата Сообщение от Элд Хасп Посмотреть сообщение
В принципе, сторона ViewModel обращённая к View должна работать только в основном потоке.
Сторона обращённая к Model работает в потоках Модели.
А внутри себя VM приводит все изменения из разных потоков к основному потоку.
Это придётся перелопачивать весь код из-за того что я не могу вывести на экран табличку!

ЭТО ПОДСТАВА!

Чего делать ребята? как мне вывести табличку на экран?
Миниатюры
Заполнение коллекции из разных потоков  
0
1498 / 892 / 325
Регистрация: 17.05.2015
Сообщений: 3,386
25.09.2019, 13:50 14
Чипс, задачу свою опишите, что делают потоки и как заполняется коллекция

Добавлено через 1 минуту
Цитата Сообщение от Чипс Посмотреть сообщение
Это придётся перелопачивать весь код из-за того что я не могу вывести на экран табличку!
ну дык вы же его писали без оглядки на последствия => ваш код тут не подходит => надо лопатить
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
25.09.2019, 13:54  [ТС] 15
Цитата Сообщение от Рядовой Посмотреть сообщение
Чипс, задачу свою опишите, что делают потоки и как заполняется коллекция
Код выложен выше Сегодня, 12:16 [ТС]
Задача:
-наполнить ObservableCollection<T>
-вывести на экран во View

Если надо, могу выложить код интефейса.

Добавлено через 1 минуту
Цитата Сообщение от Рядовой Посмотреть сообщение
ну дык вы же его писали без оглядки на последствия
Я думал, что можно будет взять другую коллекцию и вывести её на экран

Добавлено через 44 секунды
Откуда мне было знать, что есть только ObservableCollection<T> для вывода на экран коллекций!

Добавлено через 1 минуту
Цитата Сообщение от Рядовой Посмотреть сообщение
ваш код тут не подходит => надо лопатить
Я не могу, его уже слишком много! Надо какой нибудь костыль!
0
Модератор
Эксперт .NET
13798 / 10005 / 2666
Регистрация: 21.04.2018
Сообщений: 29,798
Записей в блоге: 2
25.09.2019, 13:55 16
Цитата Сообщение от Чипс Посмотреть сообщение
Чего делать ребята? как мне вывести табличку на экран?
Для вашего примера MainWindowVM
C#
21
22
23
24
25
26
27
28
29
30
        public void EventHandlerValueIsChanged(Number Number)
        {
            Console.WriteLine("Hello");
 
            Application.Current.Dispatcher.BeginInvoke
            (
                new Action(() =>Numbers.Add(new Number() {Value = Number.Value }), 
                null
            );
        }
0
1498 / 892 / 325
Регистрация: 17.05.2015
Сообщений: 3,386
25.09.2019, 13:55 17
Чипс, нужна внятная задача, а не ваш, простите, код.
Цитата Сообщение от Чипс Посмотреть сообщение
Задача:
-наполнить ObservableCollection<T>
-вывести на экран во View
это не задача
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
25.09.2019, 13:59  [ТС] 18
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Для вашего примера MainWindowVM
Что-то не так (рис 2)
Миниатюры
Заполнение коллекции из разных потоков  
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
25.09.2019, 14:09  [ТС] 19
Цитата Сообщение от Рядовой Посмотреть сообщение
это не задача
Есть код, который должен:
-по нажатию кнопки "Start" наполнить ObservableCollection<T> из разных потоков
-вывести на экран во View

Проблема в том, что ObservableCollection<T> нельзя наполнять из разных потоков, если она привязана к View.
При попытки это сделать возникает следующая ошибка (см. рис 3)

Что делать?

View

XML
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
<Window x:Class="TwoThreadInObsColl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TwoThreadInObsColl.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    
    <Window.DataContext>
        <local:MainWindowVM></local:MainWindowVM>
    </Window.DataContext>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        
        <DataGrid  Grid.Row="0" Margin="10,0,10,0" 
                  AutoGenerateColumns="False" VerticalAlignment="Top" 
                  ItemsSource="{Binding Numbers}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Наименование" Binding="{Binding Path=ProcesName}"  />
                <DataGridTextColumn Header="Номер" Binding="{Binding Path=Value}" />
            </DataGrid.Columns>
        </DataGrid>
 
        <StackPanel Grid.Row="1" HorizontalAlignment="Left">
            <Button Command="{Binding StartCommand}" Margin="5">Start</Button>
        </StackPanel>
    </Grid>
</Window>


MainWindowVM

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
public class MainWindowVM
    {
        public ObservableCollection<Number> Numbers { get; set; }
 
        private Thread ProcesThread1;
        private Thread ProcesThread2;
        Number number1;
        Number number2;
 
        public ICommand StartCommand { get; }
 
        public MainWindowVM()
        {
            Numbers = new ObservableCollection<Number>();
            number1 = new Number();
            number2 = new Number();
 
            number1.ValueIsChanged += EventHandlerValueIsChanged;
            number2.ValueIsChanged += EventHandlerValueIsChanged;
 
            StartCommand = new RelayCommand(x => Start());
        }
 
        public void EventHandlerValueIsChanged(Number Number)
        {
 
            Numbers.Add(new Number() {Value = Number.Value });
        }
 
        public void Start()
        {
            ProcesThread1 = new Thread(number1.Proces);
            ProcesThread1.Start();
 
            ProcesThread2 = new Thread(number2.Proces);
            ProcesThread2.Start();
        }
    }


Number

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Number
    {
        public int Value { get; set; }
        public int ProcesName { get; set; }
 
        public Number()
        {
            Value = 1;
        }
 
        public void Proces()
        {
            for(int i = 1; i<= 5; i++)
            {
                Value++;
                //Thread.Sleep(500); //Чтобы видно было, что работают потоки.
                ValueIsChanged?.Invoke(this);
                //Console.WriteLine("Hello");
            }
        }
 
        public event Action<Number> ValueIsChanged;
    }
Миниатюры
Заполнение коллекции из разных потоков  
0
82 / 60 / 17
Регистрация: 21.08.2015
Сообщений: 1,046
25.09.2019, 14:21  [ТС] 20
Цитата Сообщение от Рядовой Посмотреть сообщение
Чипс, нужна внятная задача
Необходимо наполнить выводимую на экран коллекцию из разных потоков.

Не знаю, как понятней объяснить =(

Задайте наводящие вопросы или скажите, чего вы не поняли?
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
25.09.2019, 14:21
Помогаю со студенческими работами здесь

Заполнение коллекции из БД
Здравствуйте, у меня есть таблица, в которой 2 поля - title и url, мне необходимо заполнить...

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

Коллекции разных типов
Добрый день! Подскажите, пожалуйста! Необходимо создать класс, который будет читать данные из...

Заполнение ListView из коллекции
Подскажите как осуществить привязку данных в ListView item source из ObservableCollection Есть...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru