Главная » Silverlight » Маршаллизация кода в поток пользовательского интерфейса

0

Как и клиентские приложения .NET (например, приложения WPF и Windows Forms), платформа Silverlight поддерживает модель однопоточного выполнения (single-threaded apartment model). В этой модели один поток управляет всем приложением и владеет всеми объектами, представляющими пользовательский интерфейс. Поток, создавший объект, владеет им. Другие потоки не могут взаимодействовать с объектом непосред­ственно. При нарушении этого правила (например, при попытке обратиться к объекту пользовательского интерфейса из другого потока) могут возникнуть блокировки, исклю­чения или более тонкие проблемы.

Для поддержания работоспособности многопоточного приложения используется объект диспетчера. Он владеет главным потоком приложения, маршаллиэирует коды (распределяет фрагменты кодов по потокам) и управляет очередью рабочих компонен­тов. При выполнении приложения диспетчер принимает рабочие запросы и выполняет их по очереди.

Примечание. Диспетчер — ЭТО экземпляр класса System. Windows. Threading. Dispatcher, впервые появившегося в WPF.

Диспетчер можно извлечь из любого элемента с помощью свойства Dispatcher. Класс Dispatcher содержит только два члена: метод CheckAccess (), позволяющий вы­яснить, может ли поток взаимодействовать с пользовательским интерфейсом, и метод Beginlnvoke (), маршаллизующий код в главный поток приложения.

Совет. В окнах подсказки Visual Studio метод Dispatcher. CheckAccess () не выводится. Тем не менее его можно использовать в коде.

Приведенный ниже код реагирует на щелчок на кнопке созданием нового объекта System. Threading. Thread. Затем созданный поток используется для запуска небольшо­го фрагмента кода, который изменяет текстовое поле на текущей странице.

private void cmdBreakRules_Click(object sender,

RoutedEventArgs e)

{

Thread thread = new Thread(UpdateTextWrong); thread.Start ();

}

private void UpdateTextWrong()

{

II С задержкой 5 секунд выполняется запись Thread.Sleep(TimeSpan.FromSeconds(5)); txt.Text = "Некоторый текст.";

}

Код обречен на неудачу. Метод UpdateTextWrong () выполняется в новом потоке, кото­рый не должен обращаться непосредственно к объектам пользовательского интерфейса Silverlight. В результате будет сгенерировано исключение UnauthorizedAccessException, нарушающее работу кода.

Чтобы исправить код, нужно получить ссылку на диспетчер, владеющий объектом TextBox (этот же диспетчер владеет страницей и другими объектами пользовательского интерфейса). Получив доступ к диспетчеру, можно вызвать метод Dispatcher. Beginlmvoke () для маршаллизации кода в поток диспетчера. Важно отметить, что ме­тод Beginlmvoke () включает код в расписание задач диспетчера. После этого диспетчер выполняет код.

Ниже приведен правильный код.

private void cmdFollowRules_Click(object sender, RoutedEventArgs e)

{

Thread thread = new Thread (UpdateTextRight) ;

thread.Start () ;

}

private void UpdateTextRight ()

{

// Задержка на 5 секунд

Thread.Sleep(TimeSpan.FromSeconds(5));                                                             .

// Получение диспетчера текущей страницы и

// обновление текстового поля

this .Dispatcher .Beginlnvoke ((ThreadStart) delegated {

txt.Text = "Некоторый текст.";

}

) ;

}

Метод Dispatcher .Beginlnvoke () принимает единственный параметр — делегат, указывающий на метод, который содержит выполняемый код. Этот метод может нахо­диться где-нибудь в другом месте приложения. Можно также использовать анонимный метод для выполнения встроенного кода (как в данном примере). Встроенный код ре­комендуется использовать для простых задач, например для обновления одной строки. Однако для решения более сложных задач лучше добавить выполняемый код в отдель­ный метод следующим образом.

private void UpdateTextRight ()

f

// Имитация работы на протяжении 5 секунд Thread.Sleep(TimeSpan.FromSeconds (5));

// Получение диспетчера текущей страницы

//и обновление текста

this.Dispatcher.Beginlnvoke(SetText);

}

private void UpdateTextRight ()

{

txt.Text = "Here is some new text.";

} ‘

Примечание. Метод Beginlnvoke () возвращает объект DispatcherOperation (в предыдущем примере он не

используется). Объект DispatcherOperation позволяет отследить статус диспетчеризации, а также выяснить,

когда код фактически выполняется. Однако объект DispatcherOperation редко бывает полезным, потому что

код, передаваемый в метод Beginlnvoke (), обычно выполняется короткое время.

. .

Трудоемкую фоновую операцию необходимо выполнять в отдельном потоке, а затем направить результат в поток диспетчера (в этот момент фактически обновляется поль­зовательский интерфейс или изменяется совместно используемый объект). Выполнять трудоемкую фоновую операцию в коде метода, передаваемого методу Beginlnvoke О , не имеет смысла. Рассмотрим немного измененный код. Он вполне работоспособен, однако в реальных задачах такой подход никогда не применяется.

private void UpdateTextRight()

{

// Получение диспетчера текущей страницы

this.Dispatcher.Beginlnvoke((ThreadStart) delegate () {

// Трудоемкая операция

Thread.Sleep(TimeSpan.FromSeconds(5));

txt.Text = "Некоторый текст.";

}

) ;

}

Проблема состоит в том, что вся работа выполняется в потоке диспетчера. Это озна­чает, что код связывает диспетчер так же, как однопоточное приложение. Зачем же было огород городить?

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии