1

Тема: Асинхронное программирование в Silverlight – исполнение в потоке UI

Асинхронное программирование в Silverlight – исполнение в потоке UI

Я разбирался с таймерами и веб-сервисами когда столкнулся с затруднением связанным с обращением к UI из фонового потока. Оказывается, такая операция запрещена. К данным UI разрешено обращаться только из потока UI. Давайте посмотрим, как вы можете обратиться к потоку UI из другого фонового потока.

Рассмотрим такую ситуацию:

Имеются какие-то данные, которые нужно модифицировать с интервалом в одну минуту. Данные загружаются веб-сервисом.

Операцию изменения вы можете производить, используя класс Timer. Вызовы таймера и веб-сервиса происходят асинхронно.

   1: ...
   2: Timer t = new Timer( GetData, null, TimeSpan.Zero, new TimeSpan( 0, 1, 0 ) );
   3: ...
   4: 
   5: public void GetData( object stateInfo )
   6: {
   7:     SampleWebServiceSoapClient client = new SampleWebServiceSoapClient();
   8:     client.HelloWorldCompleted +=
   9:         new EventHandler<HelloWorldCompletedEventArgs>( client_HelloWorldCompleted );
  10:     client.HelloWorldAsync();
  11: }
  12: 
  13: private void client_HelloWorldCompleted( object sender, HelloWorldCompletedEventArgs e )
  14: {
  15:     // update the UI
  16: }

К сожалению, этот код не будет работать. Если вы попытаетесь обратиться к UI в функции обратного вызова, будет выброшено исключение: «Invalid cross-thread access».

Итак, что же мы можем сделать, чтобы данный код работал? Как минимум есть два пути:
1.    Для обращения к потоку UI использовать класс Dispatcher.
2.    Вместо System.Threading.Timer воспользоваться System.Windows.Threading.DispatcherTimer

Thumbs up Thumbs down

2

Re: Асинхронное программирование в Silverlight – исполнение в потоке UI

Класс Dispatcher

Метода BeginInvoke класса Dispatcher значительно упрощает вызов обращение к потоку UI:

Dispatcher.BeginInvoke( Action );

Таким образом, обращение к потоку UI будет стоить вам всего одну строку кода. Допустим, есть свойство TextBlock.Text и мы хотим изменить его значение в функции обратного вызова client_HelloWorldCompleted:

   1: private void client_HelloWorldCompleted( object sender, HelloWorldCompletedEventArgs e )
   2: {
   3:     Dispatcher.BeginInvoke( () => lastUpdated.Text = DateTime.Now.ToLongTimeString() );
   4: }

Класс Dispatcher гарантирует, что данный код будет исполнен в потоке UI. Если хотите узнать подробности, прочтите эту статью MSDN о классе Dispatcher.

Thumbs up Thumbs down

3

Re: Асинхронное программирование в Silverlight – исполнение в потоке UI

Класс DispatcherTimer

DispatherTimer – это специальный таймер интегрированный в очередь класса Dispatcher, которая переоценивается каждую итерацию цикла Dispatcher. Это делает его идеальным решением для нашей задачи. Теперь давайте посмотрим, что нужно изменить в коде, чтобы использовать DispatherTimer вместо Timer.

   1: ...
   2: DispatcherTimer t = new DispatcherTimer();
   3: t.Interval = new TimeSpan( 0, 0, 1 );
   4: t.Tick += new EventHandler( RefreshData );
   5: t.Start();
   6: ...
   7: 
   8: private void RefreshData( object sender, EventArgs e )
   9: {
  10:     SampleWebServiceSoapClient client = new SampleWebServiceSoapClient();
  11:     client.HelloWorldCompleted +=
  12:         new EventHandler<HelloWorldCompletedEventArgs>( client_HelloWorldCompleted );
  13:     client.HelloWorldAsync();
  14: }
  15: 
  16: private void client_HelloWorldCompleted( object sender, HelloWorldCompletedEventArgs e )
  17: {
  18:     lastUpdated.Text = DateTime.Now.ToLongTimeString();
  19: }

DispatherTimer работает в том же потоке, что и Dispatcher, а поэтому гарантирует, что этот код будет выполнен в потоке UI.

Thumbs up Thumbs down

4

Re: Асинхронное программирование в Silverlight – исполнение в потоке UI

Резюме

Использование Dispatcher является универсальным методом исполнения кода в потоке UI. Тем не менее, иногда мы можем еще больше упростить наш код, как было показано в примере. Тщательно выбирайте методы в зависимости от ситуации.



Ссылки

Build More Responsive Apps With The Dispatcher

Dispatcher class on MSDN

DispatcherTimer class on MSDN



Оригинал статьи

Tip: Asynchronous Silverlight - Execute on the UI thread

Thumbs up Thumbs down

5

Re: Асинхронное программирование в Silverlight – исполнение в потоке UI

Вообщем как я понял, что асинхронности не получиться по любому. Вот вырезка из MSDN:

Выполнение таймеров не гарантируется в точности в тот момент, когда истекает заданный интервал времени, но они гарантировано не выполняются до истечения этого интервала. Причина этого в том, что операции DispatcherTimer помещаются в очередь Dispatcher подобно любым другим операциям. Когда будет выполнена операция DispatcherTimer, зависит от других заданий в очереди и их приоритетов.

Т.е. всё равно одновременно потоки не смогут выполняться, просто будут стоять в определённой очереди и выполняться по приоритету.
Может я не понял само определение "асинхронность"?

Thumbs up Thumbs down

6

Re: Асинхронное программирование в Silverlight – исполнение в потоке UI

Грубо говоря, асинхронность потоков – это иллюзия. У процессора имеется очередь команд, и независимо от того, сколько потоков выполняется, очередь все равно одна. Процессор можно сравнивать с официантом в ресторане, обслуживающим сразу несколько столиков, только все происходит очень быстро и пользователь не догадывается о таком внутреннем устройстве. Этой асинхронности достаточно для практически любого настольного приложения. Если интересно, прочитайте первый раздел этой главы.

В случае с DispatcherTimer частота срабатывания таймера не может быть больше частоты обновления кадров плагина, даже если задать таймеру меньшее время, поскольку в этом случае операция помещается в главный поток UI. Обычно это 60 кадров в секунду.

Thumbs up Thumbs down