Всем привет, не могли бы вы меня проконсультировать немножко в теме многопоточности?
В программе есть функционал, который я хочу реализовать в несколько потоков и (желательно)использованием пула потоков.
В общем идея проста до безобразия, но мне что-то не идет в голову как ее реализовать.
Есть класс, который считывает данные, есть класс который должен их отобразить.
Класс считывающий данные должен считывать их постоянно, и при изменении кол-ва данных, должен уведомить об этом отображающий класс, и собственно отправить последние изменения.
Для отрпавки последних изменений я создал класс отнаследованный от EventArgs, создал событие в классе которые считывает данные.
Как теперь мне их связать так, что бы при этом они работали в разных потоках?
Здравствуйте, -n1l-, Вы писали:
N>Для отрпавки последних изменений я создал класс отнаследованный от EventArgs, создал событие в классе которые считывает данные. N>Как теперь мне их связать так, что бы при этом они работали в разных потоках?
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
//код который обновляет UI
}));
IG> Application.Current.Dispatcher.BeginInvoke(
IG> DispatcherPriority.Background,
IG> new Action(() => {
IG> //код который обновляет UI
IG> }));
IG>
Что-то не могу понять, как связать?
Код который обновляет уй( ) у меня в другом классе, сам считывающий объект просто посылает данные тому классу, который обновляет уй( )
IG>p.s. судя по описанию, ты создал ObservableCollection<T> может ее и стоило сразу использовать?
Пока не могу ее использовать, нужно быстренько что-то навалять.
Здравствуйте, -n1l-, Вы писали:
N>Здравствуйте, Igor Gritsenko, Вы писали:
IG>>
IG>> Application.Current.Dispatcher.BeginInvoke(
IG>> DispatcherPriority.Background,
IG>> new Action(() => {
IG>> //код который обновляет UI
IG>> }));
IG>>
N>Что-то не могу понять, как связать? N>Код который обновляет уй( ) у меня в другом классе, сам считывающий объект просто посылает данные тому классу, который обновляет уй( )
IG>>p.s. судя по описанию, ты создал ObservableCollection<T> может ее и стоило сразу использовать? N>Пока не могу ее использовать, нужно быстренько что-то навалять.
producer -> queue -> consumer
Просто делаешь очередь с блокировкой если пустая. поток 1. пишет в очередь, 2. читает (ждет если очередь пустая), и обрабатывает данные.
Здравствуйте, -n1l-, Вы писали:
N>Здравствуйте, Igor Gritsenko, Вы писали:
N>Что-то не могу понять, как связать? N>Код который обновляет уй( ) у меня в другом классе, сам считывающий объект просто посылает данные тому классу, который обновляет уй( )
тогда в Dispatcher.Invoke можно положить сам вызов события.
либо вставить его в том "другом" при самом обновлении UI.
Обработчик события выполняется в том же потоке откуда был вызван, а Dispatcher.Invoke позволяет исполнить код в основном потоке.
т.е. можно сделать вклассе считывающем объект так :
Здравствуйте, -n1l-, Вы писали:
N>Как теперь мне их связать так, что бы при этом они работали в разных потоках?
Зависит от фреймворка/предпочтений. Самый простой вариант — использовать Rx. Например, если уже есть готовый функционал, который дёргает события в своём потоке — будет нечто вроде (по памяти, доработать напильником).
Здравствуйте, AndrewVK, Вы писали:
AVK>Лучше наверное AsyncOperation, а не голый контекст.
Ну... да, но я так и не смог оценить всё величие замысла с AsyncOp:
* "абстрагируемся" от sync context — абстракция дырявая, т.к. последний всё равно доступен через свойство.
* Теряем возможность вызвать Send(). Точнее, не теряем, просто вызываем op.SynсContext.Send(). По-моему тут AsyncOp только мешает.
* Получаем возможность проверить операцию на завершение, но не получить результат. Ну, как проверить — если при вызове op.Post() в ответ прилетел InvalidOperationException — возможно, операция таки всё.
* За операцией протаскивается UserSuppliedState, которым всё равно никто не пользуется — замыкания удобней.
А, ещё завершение операции и финалайзер AsyncOp дёргают SyncContext.OperationCompleted ... который используется только в одном наследнике AspNetSynchronizationContext, да и то для чорной внутренней магии.
Здравствуйте, -n1l-, Вы писали:
N>Всем привет, не могли бы вы меня проконсультировать немножко в теме многопоточности? N>Есть класс, который считывает данные, есть класс который должен их отобразить. N>Класс считывающий данные должен считывать их постоянно, и при изменении кол-ва данных, должен уведомить об этом отображающий класс, и собственно отправить последние изменения.
Если WinForms, то BackgroundWorker подойдёт. Не знаю, может и с WPF работать будет.
Идея такая:
— создаёшь BackgroundWorker (WorkerReportsProgress = true), указываешь (см. DoWork) ему метод, который будет выполнять чтение. Это метод и будет выполняться в отдельном потоке
— внутри метода когда нужно дёргаешь BackgroundWorker.ReportProgress(int, Object), через Object и передаёшь то, что нужно показать
— окно (основной поток) подписывается на событие BackgroundWorker.WorkerReportsProgress, получает там это Object и показывает его. Никаких заморочек с потоками — обработчик события вызывается в основном потоке!
— естественно, нужно следить, чтобы к передаваемым из BackgroundWorker данным не было одновременного доступа из нескольких потоков, или синхронизировать этот доступ. Будет проще, если отправленные через ReportProgress больше нигде не будут использоваться в читающем потоке. Например, считали порцию данных — отправили в основную форму, читаем следующую порцию.
Здравствуйте, Sinix, Вы писали:
S>Вариант 3 (.net 4 и старше) — BlockingCollection<T> и любая из Concurrent-коллекций. Пример можно глянуть тут.
BlockingCollection это слишком низкий уровень. Можно просто взять ActionBlock<T> — фоновый поток шлет в него данные, а блок обрабатывает их в контексте UI потока.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, TK, Вы писали:
TK>BlockingCollection это слишком низкий уровень. Можно просто взять ActionBlock<T> — фоновый поток шлет в него данные, а блок обрабатывает их в контексте UI потока.
О, а раз есть опыт работы — можно пример, как это будет выглядеть?
У меня вечно получалась какая-то фигня с громоздкой инициализацией блоков и постоянным рисованием на бумаге — что за чем должно идти. Rx в этом плане как-то попроще выглядит.
Здравствуйте, Sinix, Вы писали:
S>У меня вечно получалась какая-то фигня с громоздкой инициализацией блоков и постоянным рисованием на бумаге — что за чем должно идти. Rx в этом плане как-то попроще выглядит.
var block = new ActionBlock<string>(async str =>
{
await Task.Delay(1000);
listBox1.Items.Add(str);
}, new ExecutionDataflowBlockOptions { BoundedCapacity = 2, TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() });
new Thread(() =>
{
block.SendAsync("1").Wait();
block.SendAsync("2").Wait();
// или
block.AsObserver().OnNext("3");
}).Start();
В данном случае блока хватит и одного. Блок дает нам входную очередь на N элементов, поддерживает Task (т.е. можно показать пользователю модальный диалог и ждать ответа пока фоновый поток заполняет очередь).
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.