Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > WPF и Silverlight > Пауза в расчетах без паузы в отрисовке Gui


Автор: dAlex 17.5.2011, 14:02
Иногда в расчетах необходимо делать паузу (для того, чтобы успели примениться изменения) в одну-две секунды. 
Если делать 
Код

Thread.Sleep(1000);

окно при этом также подвисает. Как сделать отрисовку окна отдельно?
Функции, в которых делается пауза, вызываются так
Код

this.Dispatcher.BeginInvoke(new CheckNewStrikeDelegate(Core.CheckNewStrike), trade);

Автор: likegift 17.5.2011, 14:45
а вы что считаете в main-thread?

Автор: SergeyLoginov 17.5.2011, 15:01
Не совсем уверен, что правильно понял, но, возможно, поможет вот это:
Код

Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => { }));


Добавлено через 4 минуты и 10 секунд
А вообще для длительных вычислений вроде как используется BackgroundWorker там... А UI обновляется в Dispatcher.BeginInvoke(). 
Что-то мне подсказывает, что задача вовсе не требует пауз в одну-две секунды  smile 

Автор: dAlex 17.5.2011, 18:50
likegift, 
SergeyLoginov, да там не совсем расчеты, там биржевая торговля и в определенных местах после отправки заявки на биржу надо чуть чуть подождать, потому что принимается не сразу. То есть тут не нагрузка на процессор, но пауза нужна =)

просто у меня UI обновляется сам, после изменения полей

Автор: likegift 17.5.2011, 20:42
А нельзя в таком случае делать асинхронную отправку данных с обработкой события по завершению?

Автор: -Mikle- 17.5.2011, 21:41
Вообще, любые реализации типа "чуть чуть подождать" разрушительны. Вы не можете гарантировать, что за 1-2 секунды произойдет сетевое(!!!) ожидаемое вами событие. Всегда надо использовать способ, как написал likegift. Реализация его тоже зависит от ваших бизнес-процессов.

Автор: dAlex 18.5.2011, 09:12
likegift, 
-Mikle-, это на будущее =) но даже если так, пока будем ждать события, gui также подвиснет. Тем более, тут такая кривая система, что у нее не узнать, сделано ли действие, потому что оно, по идее, должно делаться мгновенно.

Автор: -Mikle- 18.5.2011, 13:01
Цитата(dAlex @  18.5.2011,  12:12 Найти цитируемый пост)
но даже если так, пока будем ждать события, gui также подвиснет

А это зависит от приложения уже. Как сделаешь, так и будет. События не обязаны исходить из основного потока. На данный момент, могу посоветовать только выполнять действия "операция1-подождать-операция2" в отдельном потоке (BackgroundWorker например) и синхронизировать их с основным (если нужно).

Автор: dAlex 19.5.2011, 11:42
пытаюсь делать backgroundworker, но что-то запуталась

по msdn

Код

backgroundWorker = new System.ComponentModel.BackgroundWorker();
backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);


Код

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // Do not access the form's BackgroundWorker reference directly.
            // Instead, use the reference provided by the sender parameter.
            BackgroundWorker bw = sender as BackgroundWorker;

            // Extract the argument.
            int arg = (int)e.Argument;

            // Start the time-consuming operation.
            e.Result = TimeConsumingOperation(bw, arg); //вместо нее мне надо вставить свою функцию, я так понимаю

            // If the operation was canceled by the user, 
            // set the DoWorkEventArgs.Cancel property to true.
            if (bw.CancellationPending)
            {
                e.Cancel = true;
            }
        }


но в том месте, где мне надо запустить backgroundWorker, мне мою функцию надо передать со своим параметром. Как это сделать?

Автор: likegift 19.5.2011, 13:03
Код

backgroundWorker.RunWorkerAsync(MySuperPuperParam)

Автор: dAlex 19.5.2011, 13:59
а в TimeConsumingOperation обязательно добавлять параметры bw и arg? зачем они нужны?

upd: так, с arg ясно, это как раз наш SuperPuperParam

Автор: likegift 19.5.2011, 14:11
а у вас аргумент какого типа?

Автор: dAlex 19.5.2011, 14:22
likegift, тут свой тип Trade =)

сделала так

Код

backgroundWorker = new System.ComponentModel.BackgroundWorker();
backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
backgroundWorker.RunWorkerAsync(trade);


Код

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{          
      Trade arg = (Trade)e.Argument;
      Core.CheckNewStrike(arg);
}


но такое ощущение, что  Core.CheckNewStrike вообще не выполняется

Автор: likegift 19.5.2011, 15:31
dAlex, 

TimeConsumingOperation - это просто метод из примера, без него можно жить, но с ним удобнее smile

Цитата

Core.CheckNewStrike вообще не выполняется 


а проверить то никак? на консоль там вывести или в лог..

Автор: -Mikle- 19.5.2011, 15:37
или брикпоинт поставить...

Автор: dAlex 20.5.2011, 11:08
ага, оно ругается на
Цитата

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.


я попробовала вызывать так
Код

Trade arg = (Trade)e.Argument;      
CheckNewStrikeDelegate handler = Core.CheckNewStrike;
handler(arg);


но такая же ошибка, как правильно вызвать?

Автор: -Mikle- 20.5.2011, 12:56
То что происходит в методе backgroundWorker_DoWork выполняется в отдельном потоке. Следовательно, вся цепочка методов вызываемая из него тоже выполняется в отдельном потоке. Снхронизировать вторичные потоки с основным ты уже научилась.

Автор: dAlex 20.5.2011, 13:05
а нужно Invoke или BeginInvoke?

Автор: -Mikle- 20.5.2011, 13:32
Это смотря по задаче. Если нужно, чтобы вторичный поток дождался пока отработает основной, то Invoke, а если нет (просто дать команду основному потоку), то BeginInvoke.

Автор: dAlex 20.5.2011, 14:27
тогда функция выполняется, но при паузе в ней gui также подтормаживает
Код

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{           
     Trade arg = (Trade)e.Argument;          
     this.Dispatcher.Invoke(new CheckNewStrikeDelegate(Core.CheckNewStrike), arg);                 
}

Автор: -Mikle- 20.5.2011, 14:36
Нее... ты не поняла меня... Какой смысл создавать отдельный поток, если в нем ты все что там делается отдаешь опять основному потоку? Dispatcher.Invoke надо использовать поместно, только вокруг тех участков, которые работают непосредственно с ГУИ. Вообще, в идеале, таких участков в асинхронном выполнении должно быть минимум или даже ноль.

Автор: dAlex 20.5.2011, 14:37
-Mikle-, а там же везде идет работа со свойствами, и таких участков очень много =( вся функция только и делает, что с ними работает

Автор: -Mikle- 20.5.2011, 14:42
Ну тогда нет смысла ее в отдельный поток ставить.

Добавлено через 42 секунды
А с какими свойствами? С GUI'шными?

Автор: dAlex 20.5.2011, 14:44
-Mikle-, да, с теми, что внутри Core, там кучка данных и они все отображаются на форме

Автор: -Mikle- 20.5.2011, 17:37
То есть единственная связь вторичного потока с основным потоком происходит только через событие PropertyChanged? В этом методе нет или очень мало прмых обращений к контролам (типа button1.Content, listBox1.Items.Add) ? Все происходит через биндинг? Если да, то есть одно предложение...

Автор: dAlex 23.5.2011, 09:19
-Mikle-, да, только через биндинг

Автор: -Mikle- 23.5.2011, 17:17
Тогда есть предложение: положить вызов события PropertyChanged в диспетчер.

Код

// такой метод у тебя должен уже быть в Core. Надо заменить его содержимое на это:
protected virtual void OnPropertyChanged(string propertyName)
{
    if (PropertyChanged == null) return;
    Application.Current.Dispatcher.BeginInvoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
}

Теперь, вызов установки свойств будет происходить в вызывающем потоке, а нотификация об их изменении в основном потоке приложения.
Ради эксперимента попробуй вместо асинхронного BeginInvoke использовать синхронный Invoke. Если ГУИ начнет страшно тормозить, то верни BeginInvoke.

PS: Надеюсь, что между установками свойств в коде есть значимые временные промежутки (хотя бы порядка 10 мс), иначе ГУИ будет все равно тормозить.

Автор: dAlex 24.5.2011, 11:27
сделала, все равно тормозит. Тут же дело просто не в самих расчетах и обновлении gui (так это происходит достаточно быстро), а в присутствии этой паузы. Как раз пока она происходит, gui и подвисает. Надо ее как-то вызвать по-другому, но как?

Автор: -Mikle- 24.5.2011, 15:55
Так, минуту... Ты вызываешь свой хитрый метод из бэкграундворкер?
Ты убрала код с диспетчером из бэкграундворкер? То есть щас в lalala_DoWork стоит просто вызов без Dispatcher.Invoke?

Автор: dAlex 25.5.2011, 10:13
нет, не из воркера, а как раньше, сразу Dispatcher.Invoke. Попробую с воркером =)

Автор: dAlex 25.5.2011, 10:39
а нужно все-таки просто вызов без Dispatcher.Invoke или с ним?
сейчас так
Код

//в Core
protected virtual void OnPropertyChanged(string propertyName)
       {
            if (PropertyChanged == null) return;
            Application.Current.Dispatcher.BeginInvoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
       }  

Код

//в Window
backgroundWorker = new System.ComponentModel.BackgroundWorker();
backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
backgroundWorker.RunWorkerAsync(trade);



Код


        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {          
            Trade arg = (Trade)e.Argument;   
            Core.CheckNewStrike(arg);            
            
        }


в итоге снова ругань на 
Цитата

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

Автор: -Mikle- 25.5.2011, 12:05
Цитата(dAlex @  25.5.2011,  13:39 Найти цитируемый пост)
сейчас так

Да, именно так я и предлогал.

Цитата(dAlex @  25.5.2011,  13:39 Найти цитируемый пост)
в итоге снова ругань 

Значит есть еще где-то места, в которых из метода CheckNewStrike идет обращение к GUI, потому что PropertyChanged, на которых сидит биндинг, стабильно вызывается в потоке GUI. Посмотри внимательно еще раз.

Автор: dAlex 25.5.2011, 13:12
а это может быть связано с тем, что backgroundWorker вызывается из таймера? первая ругань происходит в строчке, где меняется Cc_collection, а это точно свойство

upd: 
в строке типа такой
Код

Summator[Cnum].a3 = ...

всё в порядке, а если делать
Код

Cc_collection.Add(..)

как раз и вылезает ексепшн. Может, там нужен не PropertyChanged, а именно CollectionChanged? раз Add

Автор: -Mikle- 25.5.2011, 13:46
Если строчка Cc_collection.Add(..) имеется где-нить в иерархии вызовов методов начиная с метода CheckNewStrike, то да, проблема в этом. Если таких строчек не много, то передайте только эту одну строчку в синхронное выполнение GUI - Dispatcher.Invoke(......Cc_collection.Add(..) .....)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)