События и многопоточность
От: -n1l-  
Дата: 13.06.13 05:41
Оценка:
Всем привет, не могли бы вы меня проконсультировать немножко в теме многопоточности?
В программе есть функционал, который я хочу реализовать в несколько потоков и (желательно)использованием пула потоков.
В общем идея проста до безобразия, но мне что-то не идет в голову как ее реализовать.
Есть класс, который считывает данные, есть класс который должен их отобразить.
Класс считывающий данные должен считывать их постоянно, и при изменении кол-ва данных, должен уведомить об этом отображающий класс, и собственно отправить последние изменения.
Для отрпавки последних изменений я создал класс отнаследованный от EventArgs, создал событие в классе которые считывает данные.
Как теперь мне их связать так, что бы при этом они работали в разных потоках?
Re: События и многопоточность
От: Igor Gritsenko Россия http://gritsenko.biz
Дата: 13.06.13 06:02
Оценка: 1 (1)
Здравствуйте, -n1l-, Вы писали:

N>Для отрпавки последних изменений я создал класс отнаследованный от EventArgs, создал событие в классе которые считывает данные.

N>Как теперь мне их связать так, что бы при этом они работали в разных потоках?


  Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background,
    new Action(() => {
     //код который обновляет UI 
    }));


p.s. судя по описанию, ты создал ObservableCollection<T> может ее и стоило сразу использовать?
Re[2]: События и многопоточность
От: -n1l-  
Дата: 13.06.13 06:31
Оценка:
Здравствуйте, Igor Gritsenko, Вы писали:

IG>
IG>  Application.Current.Dispatcher.BeginInvoke(
IG>    DispatcherPriority.Background,
IG>    new Action(() => {
IG>     //код который обновляет UI 
IG>    }));
IG>


Что-то не могу понять, как связать?
Код который обновляет уй( ) у меня в другом классе, сам считывающий объект просто посылает данные тому классу, который обновляет уй( )

IG>p.s. судя по описанию, ты создал ObservableCollection<T> может ее и стоило сразу использовать?

Пока не могу ее использовать, нужно быстренько что-то навалять.
Re[3]: События и многопоточность
От: vvlad.net  
Дата: 13.06.13 07:29
Оценка: -1
Здравствуйте, -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. читает (ждет если очередь пустая), и обрабатывает данные.
Re[3]: События и многопоточность
От: Igor Gritsenko Россия http://gritsenko.biz
Дата: 13.06.13 08:39
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>Здравствуйте, Igor Gritsenko, Вы писали:


N>Что-то не могу понять, как связать?

N>Код который обновляет уй( ) у меня в другом классе, сам считывающий объект просто посылает данные тому классу, который обновляет уй( )

тогда в Dispatcher.Invoke можно положить сам вызов события.
либо вставить его в том "другом" при самом обновлении UI.

Обработчик события выполняется в том же потоке откуда был вызван, а Dispatcher.Invoke позволяет исполнить код в основном потоке.
т.е. можно сделать вклассе считывающем объект так :

  Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background,
    new Action(() => {
     
if(eventHandler != null){
  eventHandler(this,eventArgs);
}
    }));


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


public void OnUpdateEvent(object sender, SourceCahngedEventArgs e)
{
  Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background,
    new Action(() => {
      listBox.Add(e.NewElement);
    }));
}


тогда при вызове обработчика из любого потока элементы в Listbox будут добовляться в основном потоке.
Re[4]: События и многопоточность
От: -n1l-  
Дата: 14.06.13 03:23
Оценка:
Application.Current.Dispatcher.BeginInvoke
Это WPF? А без wpf'a есть что-то? Что-то общее, что бы мне подошло?
Re: События и многопоточность
От: Sinix  
Дата: 14.06.13 05:28
Оценка: +2
Здравствуйте, -n1l-, Вы писали:

N>Как теперь мне их связать так, что бы при этом они работали в разных потоках?


Зависит от фреймворка/предпочтений. Самый простой вариант — использовать Rx. Например, если уже есть готовый функционал, который дёргает события в своём потоке — будет нечто вроде (по памяти, доработать напильником).
// Вызывается в UI-потоке
Observable
  .FromEventPattern(
    (EventHandler<SomeEventArgs> h) => myService.SomeEvent += h,
    (EventHandler<SomeEventArgs> h) => myService.SomeEvent -= h)
  .ObserveOn(SyncronizationContext.Current)
  .Subscribe(args =>
  {
    // Some code.
  })


Вариант 2 — передаёте в ваш многопоточный код SynchronisationContext из UI-потока и
// заменяем
handler(this, someEventArgs);
// на
syncContext.Post(() => handler(this, someEventArgs));


Вариант 3 (.net 4 и старше) — BlockingCollection<T> и любая из Concurrent-коллекций. Пример можно глянуть тут.
Re[2]: События и многопоточность
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.06.13 10:27
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Вариант 2 — передаёте в ваш многопоточный код SynchronisationContext из UI-потока и


Лучше наверное AsyncOperation, а не голый контекст.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: События и многопоточность
От: Sinix  
Дата: 14.06.13 11:28
Оценка: +1
Здравствуйте, AndrewVK, Вы писали:

AVK>Лучше наверное AsyncOperation, а не голый контекст.

Ну... да, но я так и не смог оценить всё величие замысла с AsyncOp:
* "абстрагируемся" от sync context — абстракция дырявая, т.к. последний всё равно доступен через свойство.
* Теряем возможность вызвать Send(). Точнее, не теряем, просто вызываем op.SynсContext.Send(). По-моему тут AsyncOp только мешает.
* Получаем возможность проверить операцию на завершение, но не получить результат. Ну, как проверить — если при вызове op.Post() в ответ прилетел InvalidOperationException — возможно, операция таки всё.
* За операцией протаскивается UserSuppliedState, которым всё равно никто не пользуется — замыкания удобней.

А, ещё завершение операции и финалайзер AsyncOp дёргают SyncContext.OperationCompleted ... который используется только в одном наследнике AspNetSynchronizationContext, да и то для чорной внутренней магии.

Вот что они этим хотели сказать?
Re: События и многопоточность
От: Poul_Ko Казахстан  
Дата: 17.06.13 03:03
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>Всем привет, не могли бы вы меня проконсультировать немножко в теме многопоточности?

N>Есть класс, который считывает данные, есть класс который должен их отобразить.
N>Класс считывающий данные должен считывать их постоянно, и при изменении кол-ва данных, должен уведомить об этом отображающий класс, и собственно отправить последние изменения.

Если WinForms, то BackgroundWorker подойдёт. Не знаю, может и с WPF работать будет.
Идея такая:
— создаёшь BackgroundWorker (WorkerReportsProgress = true), указываешь (см. DoWork) ему метод, который будет выполнять чтение. Это метод и будет выполняться в отдельном потоке
— внутри метода когда нужно дёргаешь BackgroundWorker.ReportProgress(int, Object), через Object и передаёшь то, что нужно показать
— окно (основной поток) подписывается на событие BackgroundWorker.WorkerReportsProgress, получает там это Object и показывает его. Никаких заморочек с потоками — обработчик события вызывается в основном потоке!
— естественно, нужно следить, чтобы к передаваемым из BackgroundWorker данным не было одновременного доступа из нескольких потоков, или синхронизировать этот доступ. Будет проще, если отправленные через ReportProgress больше нигде не будут использоваться в читающем потоке. Например, считали порцию данных — отправили в основную форму, читаем следующую порцию.
Brainbench transcript #6370594
Re[5]: События и многопоточность
От: TK Лес кывт.рф
Дата: 17.06.13 05:33
Оценка:
Здравствуйте, -n1l-, Вы писали:

N> Application.Current.Dispatcher.BeginInvoke

N>Это WPF? А без wpf'a есть что-то? Что-то общее, что бы мне подошло?

Общий вариант можно описать так:

[c#]
async Task Process(cancellationToken, eventObserver)
{
while (!cancellationToken.IsCancellationRequested())
{
var data = await GetDataFromOtherThread();
eventObserver.OnNext(data);
}
}
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[2]: События и многопоточность
От: TK Лес кывт.рф
Дата: 17.06.13 05:36
Оценка: 8 (1) +1
Здравствуйте, Sinix, Вы писали:

S>Вариант 3 (.net 4 и старше) — BlockingCollection<T> и любая из Concurrent-коллекций. Пример можно глянуть тут.


BlockingCollection это слишком низкий уровень. Можно просто взять ActionBlock<T> — фоновый поток шлет в него данные, а блок обрабатывает их в контексте UI потока.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[3]: События и многопоточность
От: Sinix  
Дата: 17.06.13 06:22
Оценка:
Здравствуйте, TK, Вы писали:

TK>BlockingCollection это слишком низкий уровень. Можно просто взять ActionBlock<T> — фоновый поток шлет в него данные, а блок обрабатывает их в контексте UI потока.

О, а раз есть опыт работы — можно пример, как это будет выглядеть?

У меня вечно получалась какая-то фигня с громоздкой инициализацией блоков и постоянным рисованием на бумаге — что за чем должно идти. Rx в этом плане как-то попроще выглядит.
Re[4]: События и многопоточность
От: TK Лес кывт.рф
Дата: 17.06.13 08:49
Оценка: 24 (1)
Здравствуйте, 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 (т.е. можно показать пользователю модальный диалог и ждать ответа пока фоновый поток заполняет очередь).
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.