Posted on 26. June 2010

Синхронизация данных между потоками в Silverlight

Read this article in your language IT | EN | DE | ES

В Silverlight, по-умолчанию, весь пользовательский код выполняется в потоке интерфейса. Тем самым при длительных операциях мы может заблокировать не только Silverlight приложение, но и весь браузер. Что является не дружественным поведенем нашего приложения по отношеню к пользователям. Для решения этой проблемы можно запускать паралельные потоки или использовать пул потоков. И уже непосредственно в паралельных потоках запускать длительные по времени операции. При этом в интерфейсе можно отобразить индикатор работы процесса(например BusyIndicator).

Для эмуляции длительной операции создадим новое Silverlight приложение и в коде добавим следующее

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;

namespace Multithreading.Dispatcher
{
	public partial class MainPage : UserControl
	{
		public MainPage()
		{
			InitializeComponent();
			Loaded += new RoutedEventHandler(MainPage_Loaded);
		}

		void MainPage_Loaded(object sender, RoutedEventArgs e)
		{
			for (int z = 0; z < 1000000; z++)
			{
				for (int i = 0; i < 1000000; i++)
				{
					int j = i;
				}
			}
		}
	}
}

При запуске такого приложения мы получем заблокированное окно браузера:

Длительная операция в потоке интерфейса

Чтобы решить эту проблему изменим наше тестовое приложение так, чтобы длительная операция выполнялась в другом потоке:

void MainPage_Loaded(object sender, RoutedEventArgs e)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
				{
					for (int z = 0; z < 100000; z++)
					{
						for (int i = 0; i < 10000; i++)
						{
							int j = i;
						}
					}

				}), null);

			busyIndicator.IsBusy = true;
		}

Результат работы наших изменений смотрите ниже:

Длительная операция в паралельном потоке с индикатором процесса

Но здесь мы получаем проблему синхронизации данных между потоком интерфейса и "рабочим" потоком. Например в нашем примере нам необходимо спрятать BusyIndicator по окончанию длительной операции. Для решения этой задачи инфраструктура Silverlight предоставляет несколько способов:

Использование Dispatcher

Dispatcher предоставляет возможность выполнения кода в потоке интерфейса из неинтерфейсного потока. Для нашего примера нужно изменить код на следующий:

void MainPage_Loaded(object sender, RoutedEventArgs e)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
				{
					for (int z = 0; z < 100000; z++)
					{
						for (int i = 0; i < 10000; i++)
						{
							int j = i;
						}
					}

					Dispatcher.BeginInvoke(new Action(() =>
					{
						busyIndicator.IsBusy = false;
					}));
				}), null);

			busyIndicator.IsBusy = true;
		}

 Через метод BeginInvoke() мы указываем какой код необходимо выполнить в потоке интерфейса.

Использование SynchronizationContext

SynchronizationContext обеспечивает базовую функциональность для синхронизации контекста в различных моделях синхронизации. Данный подход является более мощным, чем использование Dispatcher. Например он позволяет выполнять код синхронно или асинхронно в потоке интерфейса по отношению к текущему потоку. При использовании контекста синхронизации его необходимо передать в "рабочий" поток. Пример:

void MainPage_Loaded(object sender, RoutedEventArgs e)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
				{
					for (int z = 0; z < 100000; z++)
					{
						for (int i = 0; i < 10000; i++)
						{
							int j = i;
						}
					}

					((SynchronizationContext)obj).Post(new SendOrPostCallback((parameter) =>
						{
							busyIndicator.IsBusy = false;
						}), null);
				}), SynchronizationContext.Current);

			busyIndicator.IsBusy = true;
		}

Исходный код:



Comments

Александр Александр Russia
5:07 PM on Wednesday, February 16, 2011

Спасибо статью Сергей!
Статья помогла вызывать синхронно WCF сервисы из Silverlight без "зависона" GUI.

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading