Windows 8 und die Zukunft von Silverlight

by St. Lange 27. September 2011 17:30

Der Nachfolger von Windows 7

Zunächst einmal wird auf Windows 8 alles laufen, was auch auf der Vorgängerversion läuft. Es gab ja einige Befürchtungen, dies könnte nicht so sein.

Neben vielen wirklich coolen Neuerungen wie Hyper-V auf dem Client, RemoteFX oder Windows To Go, ist die neue Oberfläche im Metro Design und die ihr zugrundeliegende Windows Runtime das innovative Highlight von Windows 8. Neben dem gewohnten Windows Desktop, der auch weiterhin zur Verfügung stehen wird, gibt es die neue Metro Oberfläche, die speziell auch Touch-Geräte unterstützt und teilweise schon vom Windows Phone bekannt ist.

Grundlegend neu ist die Windows Runtime, die die Programmierschnittstelle für Metro-Apps bereitstellt und technologisch neue Wege einschlägt.

Die Windows Runtime

Die schlicht Windows Runtime (WinRT) genannte Schnittstelle zum Betriebssystem ist ein ABI (Application Binary Interface) nach dem Vorbild von COM. Die WinRT ist in nativem Code mit C++ geschrieben und darauf ausgelegt, das UI „fast and fluid“ zu machen. Um sie zu programmieren, wurde C++ mit vielen neuen Features angereichert, die man aus .NET kennt: Properties, Exceptions, Delegates, Events, Generics, Lambda-Expression, u.a. Die dazugehörige Syntax ist praktisch identisch mit C++/CLI, nur wird eben kein managed, sondern nativer Code generiert. Die WinRT stellt ihre Funktionalität in Form von Komponenten, die Interfaces implementieren, zur Verfügung. Um aus verschiedenen Programmiersprachen heraus genutzt werden zu können, legt sie bestimmte Datentypen und Konventionen fest. Das ist ganz ähnlich wie die CLR in .NET funktioniert, aber die WinRT basiert nicht auf .NET. Sie verwendet insbesondere kein Garbage-Collection, sondern das aus COM bekannte Reference-Counting. Um die Programmierung zu vereinfachen wurde dazu in C++ ein neuer new-Operator eingeführt, der den Compiler veranlasst, die Aufrufe von AddRef und Release selbst zu generieren, wenn der Pointer auf ein so erzeugtes Objekt verwendet wird.

Die in COM verwendete IDL (Interface Definition Language) zur Beschreibung von Schnittstellen wird nicht eingesetzt. Stattdessen kommt ein moderneres Metadatenformat zum Einsatz, welches eine Weiterentwicklung des aus .NET bekannten Metaformats darstellt. Aufgrund dieser Windows Metadata (WinMD) kann die WinRT von beliebigen Sprachen aus anprogrammiert werden (im Gegensatz zu .NET, was nur von beliebigen managed Sprachen aus angesprochen werden kann). Zurzeit sind C++ und JavaScript die einzigen unmanaged Sprachen und C# und VB die einzigen managed Code Sprachen, die zur Verfügung gestellt werden.

Das Binden an eine konkrete Programmiersprache wird als „Projection“ bezeichnet, wohl um auszudrücken, dass es mehr ist als simples „Binding“. Da die WinRT sich über ihre Metadaten selbst beschreibt, ist die Projection in eine konkrete Sprache automatisiert und sehr effizient möglich. Sprachen wie C# können diese Metadaten praktisch direkt nutzen und benötigen daher die in COM notwendigen Interop-Assemblies nicht mehr.

Die Projection erlaubt außerdem eine tiefe Integration in die jeweilige Programmiersprache. So sind beispielsweise ca. 10 bis 15 Prozent der WinRT Funktionen asynchron, d.h. sie starten einen länger andauernden Vorgang und liefern gleichzeitig ein Interface auf ein Operation-Objekt zurück, über das man diesen Vorgang kontrollieren und am Ende dessen Ergebnis abfragen kann. Ruft man eine solche Funktion aus C# heraus aus, kann man direkt das neuen Schlüsselwort await aus dem async/await Pattern von C# 5 verwenden. Aus dem IAsyncOperation der WinRT wird ein Task<T> in C#. In JavaScript dagegen wird die gleiche WinRT-Funktion in ein entsprechendes Promise-Konstrukt umgewandelt.

Programmiermodell

Das folgende Schaubild ist eine der Kernfolien von Microsoft.

Es zeigt deutlich, dass die Metro Apps neben dem bisherigen Windows Desktop Apps liegen, sie also um etwas Neues ergänzen und nichts ersetzen.

Die von der WinRT angebotenen Klassen und Funktionen sehen Silverlight zum Verwechseln ähnlich und jeder Silverlight- bzw. WPF-Programmierer findet sich praktisch sofort zurecht. Zwar sind die Namespaces und viele Details etwas anders, aber die aus Silverlight bekannten Controls und Konzepte wie XAML, Resources, Styles, Templates, etc. werden alle nahezu identisch unterstützt. Auch das Resource-Format .resw ist praktisch das Gleiche wie .resx.

Die Konzepte der WinRT eignen sich hervorragend für alle Arten von Schnittstellen, stehen aber zunächst mal nur unter Windows 8 und nur für Metro Apps zur Verfügung.

C++/CX

C++/CX ist der Name des für die WinRT erweiterten C++. Stark vereinfacht kann man es so ausdrücken: C++ plus die coolen Sprachfeatures von C# mit Reference Counting anstatt Garbage Collection. Da C++/CX nativen Code erzeugt, kann man nun die unschlagbare Performance von C++ mit den modernen Sprachmitteln von C# nutzen. Ein gewaltiger Schritt für C++ als Basissprache moderner Betriebssysteme. Microsoft möchte, dass C++/CX in den ANSI-Standard einfließt. Zurzeit kann man C++/CX allerdings nur unter Windows 8 und nur für WinRT Metro Apps verwenden.

JavaScript

Auch für JavaScript ist die WinRT verfügbar. Dadurch sind mit HTML5 Browser-basierte Metro-Anwendungen möglich, die alle Features des Betriebssystems nutzen können. Ich sehe zwei große Vorteile dieses Ansatzes. Zum einen findet das riesige Heer von JavaScript-Programmierern nun einen einfachen Zugang zur Programmierung von Windows Apps. Ein kluger Schachzug von Microsoft, der auch von Microsofthassern nur schwer zu kritisieren ist. Zum anderen lässt sich dynamischer Content aus dem Web leichter in eine HTML5- als in eine XAML-basierte Metro App integrieren.

Bevor nun jemand wegen HTML5 auf falsche Gedanken kommt: Alle Metro Apps basieren auf der WinRT. HTML5 Apps werden zwar vom IE 10 gehostet, laufen aber ausschließlich auf Windows 8 Geräten und sind damit genauso proprietär wie eine XAML-basierte Metro App.

C#/VB

Als .NET Sprachen stehen zurzeit C# und VB zur Verfügung. Auch wenn das obige Schaubild den Eindruck erweckt, als würde C# irgendwie ohne .NET Framework laufen, gibt es genau wie bisher eine CLR, Garbage Collection, IL Code und ein .NET Framework. Tatsächlich ist auf Windows 8 auch nur ein .NET 4.5 Framework installiert, mit dem man wahlweise WinForms, ASP.NET, WPF, etc. Anwendungen erstellen kann oder alternativ auf Basis der WinRT Metro Apps. Eine Vermischung der beiden Welten innerhalb einer Anwendung ist nicht vorgesehen.

Ansonsten hat man das Gefühl, Silverlight oder WPF zu programmieren. XAML, Controls, Events, Styles, Templates, ResourceDictionaries, VisualStateManager, usw. sind bis ins Detail sofort vertraut. Natürlich gibt es etliche Unterschiede, beispielsweise bei den Touch-Events oder bei neuen Namensräumen, aber jeder Silverlight-Entwickler findet sich schnell zurecht.

Es ist auch möglich, mit C#/VB neue WinRT-kompatible Komponenten zu erstellen, wenn man sich an bestimmte Konventionen hält. So kann man beispielsweise nützliche Hilfsroutinen, sein ViewModel oder neue Controls in C# schreiben und diese dann in einer HTML5 App über JavaScript verwenden. So muss das auch sein. Ich bin absolut überzeugt, dass .NET die dominierende Technologie für WinRT Anwendungen werden wird, weil .NET einfach die produktivste Art ist, Software zu entwickeln, die bisher erfunden wurde.

Performance zählt

Eine der wichtigsten Neuerungen der WinRT sind drastische Verbesserungen der UI-Performance gemäß dem Metro-Paradigma „fast and fluid“. Am einfachsten lässt sich das anhand eines Beispiels verdeutlichen. In Silverlight beruhen Animationen letztlich darauf, dass man über eine Timeline gesteuert eine oder mehrere Dependency-Properties über einen Zeitraum hinweg verändert. Basierend auf diesen Änderungen rendert der UI-Thread die Oberfläche mehrmals pro Sekunde neu. Auf einem Intel Desktop-Prozessor ist dies kein Problem, wenn auch ein Blick in den Taskmanager zeigt, dass damit ein Prozessorkern oft zu 100% ausgelastet ist. Auf einem mobilen Gerät mit einem ARM Prozessor führt dies jedoch zu einem unlösbaren Problem. Die Animation wirkt entweder ruckelig oder benötigt bestenfalls einfach zu viel Energie und belastet damit die Batterie unangemessen. Dies war einer der Hauptgründe, warum Microsoft vor rund 3 Jahren Silverlight nicht auf Windows Mobile sinnvoll zum Laufen bekommen hat. Andererseits erwartet ein Anwender heutzutage, das beispielsweise ein zu löschendes Element aus einer Liste nicht einfach „wegpoppt“, sondern sich langsam ausblendet, während die dahinterliegenden Elemente nachrücken und die Lücke schließen. Grundsätzlich kein Problem für ein Silverlight FluidMoveBehavior, aber leider sind diese zu performancehungrig für mobile Geräte.

Die WinRT geht daher mit fest eingebauten Animationen für Standard-Controls einen neuen Weg. Für das obige Beispiel aktiviert man einfach die sog. Theme-Transition AddDelete aus der eingebauten Animations-Library. Etwas vereinfacht ausgedrückt rendert diese Animation alle beteiligten List-Items genau einmal und bewegt sie dann GPU-beschleunigt über das Display, ohne dass der Inhalt dabei neu berechnet werden müsste. Visuell macht dies keinerlei Unterschied, sieht geschmeidig aus und braucht nur einen Bruchteil der Energie. Die vorgefertigten Animationen sind vielfältig und lassen sich auch untereinander kombinieren. Wem das dennoch nicht ausreicht, kann die jetzt als „Dependent Animation“ bezeichneten bisherigen Animationen immer noch verwenden. Allerdings muss man sie explizit einschalten, damit einem auch bewusst wird, dass man hier Leistung verbrät.

Dieses Beispiel zeigt sehr deutlich, wie sehr die WinRT auf Geschwindigkeit, flüssiges UI und Energieeffizienz getrimmt ist. Der Verzicht auf Garbage-Collection in der WinRT erfolgte übrigens nicht aufgrund von Performanceüberlegungen. Zum einen wollte man auf Betriebssystemebene die vollständige Kontrolle über die Speicherverwaltung haben und zum anderen sollte die WinRT wirklich von jeder Programmiersprache aus genutzt werden können.

Weitere Programmiersprachen

C++, JavaScript, C# und VB sind zurzeit die einzigen verfügbaren Programmiersprachen für die Windows Runtime und man kann damit auch nur Metro Apps bauen. Da jedoch alle Entwickler, mit denen ich auf der BUILD gesprochen habe, völlig begeistert sind von der WinRT, vermute ich mal, dass in naher Zukunft diverse Programmiersprachen auf die WinRT aufgesetzt werden. Einfach so, weil es eine interessante Herausforderung ist und Entwickler gerne spannende neue Dinge machen. Außerdem lassen sich viele Sprachen vermutlich sogar leichter auf die WinRT aufsetzen als auf das .NET Framework.

Silverlight

Zur strategischen Zukunft von Silverlight wurde auf der BUILD nichts gesagt. Natürlich läuft Silverlight im Browser und Out-of-Browser auf dem Windows 8 Desktop. Das war aber sowieso klar, da schon immer jede neue Windows-Version die Software der Vorgängerversion ausführen konnte. Auch das Silverlight, Flash und sonstige PlugIns nicht innerhalb einer HTML5 Metro App laufen werden ist logisch. Metro Apps laufen in einer Sandbox und werden vor dem Verkauf über den Windows Marketplace von Microsoft verifiziert. Und zu diesem Konzept passen nun mal keine Browser-PlugIns, auch nicht Silverlight.

Nun ist Metro zwar technisch gesehen eine Weiterentwicklung von Konzepten aus Silverlight, deckt aber einen größtenteils anderen bzw. neuen Markt ab. Metro steht für eine neue UX auf Windows 8 Geräten und läuft weder auf Windows XP, Vista oder 7, noch auf dem Desktop weder im Browser noch Out-of-Browser, noch auf dem Mac. Damit ist es auch mittelfristig keine Alternative für Projekte, die man heute mit Silverlight machen würde. Silverlight hat sich von der aus heutiger Sicht nicht mehr realistischen „WPF/Everywhere“ Idee hin zu einer Browser-basierten Windows Desktop Technologie entwickelt. Durch die Einführung von PInvoke kann eine Full-Trust Silverlight 5 Anwendung sowohl im Browser als auch Out-of-Browser alles machen, was mit Win32 möglich ist. Das ist eine andere Richtung als die von Metro eingeschlagene. Metro erweitert Windows für neue Gerätetypen, ersetzt aber keine der bisherigen Desktop-Technologien.

Ich vermute, dass Microsoft selbst noch nicht so genau weiß bzw. entschieden hat, was nach Silverlight 5 kommen wird. Viele Firmen haben auf Silverlight gesetzt und auch LightSwitch basiert darauf. Daher bin ich sicher, dass Silverlight auch in den nächsten Jahren eine zunehmend wichtigere Rolle spielen wird. Für reine Web-Anwendungen mit maximaler Reichweite muss man ausschließlich auf HTML und JavaScript setzen, das war aber auch schon bei Einführung von Silverlight so und Silverlight war nie als Alternative zu HTML angedacht. In Szenarien, wo man jedoch die Wahl zwischen HTML oder Silverlight hat, ist und bleibt Silverlight fast immer die bessere Alternative.

Die tägliche Arbeit

Metro und die WinRT sind sowohl von der UX Seite als auch technologisch ein großer Schritt nach vorne. Die Zukunft wird sicherlich sehr spannend werden. Bis Windows 8 auf dem Markt ist und eine nennenswerte Verbreitung hat, wird allerdings noch eine ganze Weile vergehen. Wer also nächsten Monat ein neues Projekt startet und sich heute Gedanken über die Technologie macht, für den hat sich durch die BUILD nicht viel geändert. Er sollte Silverlight oder WPF für den Desktop verwenden, genau wie bisher. Zwei sehr positive Dinge für die unmittelbare Zukunft hat die BUILD allerdings schon gebracht: Zum einen weiß jetzt jeder Silverlight-Entwickler, dass er sein Know-how zur UI-Programmierung, XAML oder RIA Services auch langfristig mit Metro weiter nutzen kann. Zum anderen sind die von Microsoft durch eine unglückliche Präsentation von Windows 8 im Juni geschürten Befürchtungen, die .NET Entwicklung würde durch JavaScript ersetzt, endlich vom Tisch.

Ich vermute, dass die WinRT langfristig das Win32 API verdrängen wird, jedoch frühestens in der übernächsten Windows-Version. Vorher werden aber bestehende Systeme wie Windows Phone 8 die WinRT als Basis erhalten. Microsoft hat jedenfalls eine sehr durchdachte und innovative Grundlage für die nächsten Jahre geschaffen.

Wenn ich mir was wünschen könnte: Silverlight 6 im Metro Look mit einer WinRT als PlugIn für weitere non-Microsoft Plattformen. Dies wird aber wohl ein Wunsch bleiben.

Tags: , ,

.net | Silverlight

dotnet Cologne 2011 fast ausgebucht

by St. Lange 21. March 2011 19:10

Nachdem letzte Woche Dienstag die Anmeldung für die dotnet Cologne 2011 freigeschaltet wurde, waren der Super Early Bird nach weniger als 5 Minuten und der Early Bird nach weniger als 3 Stunden weg. Auch das neu überarbeitete Anmeldesystem hat damit seinen ersten schweren Lasttest mit Erfolg bestanden.

Nun, eine Woche später ist die Konferenz schon fast ausgebucht – es sind nur noch 14 Plätze frei (Stand 19:00h). Heute haben wir die Sprecher und Vorträge nochmals aktualisiert, sodass sie nun fast komplett sind.

Wer also noch einen Platz bekommen möchte, sollte nicht mehr allzulange warten, sondern sich lieber gleich anmelden.

An dieser Stelle möchte ich auch allen Bewerbern für die zahlreichen Session-Vorschläge danken. Wir hatten sehr viel mehr Einreichungen als wir berücksichtigen konnten.

Alle Sprecher treten übrigens ohne Honorar an – auch dafür schon mal ein Dankeschön für das Engagement!

Insbesondere freut es mich, Bart De Smet als Speaker gewonnen zu haben. Bart ist Software Development Engineer im Cloud Programmability Team der Microsoft Corporation und wird Vorträge zum Reactive Extension Framework und zu LINQ halten. Ich habe ihn auf der VSone im Februar in München kennengelernt. Als wir ihm von der dotnet Cologne erzählten, war er sofort bereit seinen privaten Heimaturlaub Anfang Mai in Brüssel zu unterbrechen und zur Konferenz zu kommen.

Es ist schon toll zu sehen, wie gut das Konzept der Community Konferenz „von Entwicklern für Entwickler“ funktioniert.

Tags:

.net

dotnet Cologne 2011 – Call for Papers

by St. Lange 4. January 2011 11:50

Am 6. Mai findet im MediaPark Köln die dotnet Cologne 2011 statt, die größte deutsche .NET Community Konferenz für Software-Entwickler, organisiert von den .NET User Groups Köln und Bonn.

Zurzeit suchen wir noch Sprecher mit interessanten Themen aus allen Bereichen der .NET Technologien. Wer also einen Vortrag einreichen möchte, sollte nicht bis kurz vor Einreichungsschluss warten, sondern dies möglichst zeitnah tun. Wie das im Einzelnen geht, steht hier. Wir freuen uns auf Eure Vorschläge.

Für alle, die Fragen, Ideen, Anregungen oder Wünsche haben, stehen Albert, Roland und ich gerne zur Verfügung.

Tags:

.net

dotnet Cologne 2011

by St. Lange 22. November 2010 15:45

Am 6. Mai findet im MediaPark Köln die dotnet Cologne 2011 statt, die größte deutsche .NET Community Konferenz für Software-Entwickler, organisiert von den .NET User Groups Köln und Bonn.

Zurzeit suchen wir noch Sprecher mit interessanten Themen aus allen Bereichen der .NET Technologien. Wer also einen Vortrag einreichen möchte, sollte nicht bis kurz vor Einreichungsschluss warten, sondern dies möglichst zeitnah tun. Wie das im Einzelnen geht, steht hier. Wir freuen uns auf Eure Vorschläge.

Für alle, die Fragen, Ideen, Anregungen oder Wünsche haben, stehen Albert, Roland und ich gerne zur Verfügung.

Tags:

.net

Silverlight Tipp der Woche: .restext statt .resx Dateien für Text–Ressourcen

by St. Lange 4. September 2010 10:48

In diesem Tipp geht es um die Verwaltung von String Ressourcen in .restext Dateien, welche beispielsweise bei der Lokalisierung Vorteile gegenüber üblichen .resx Dateien haben.

Zusammenfassung

Beim Verwalten von Texten als Ressourcen beispielsweise für die Lokalisierung werden üblicherweise .resx Dateien verwendet. Für Texte sind Dateien im .restext Format jedoch oft besser geeignet.

Beschreibung

Der Standardweg zum Verwalten von Ressourcen in .NET sind die XML-basierten .resx Dateien. Mit ihnen können Daten aller Art als sog. Embedded Resources in Assemblies eingebettet werden. In .NET gibt es aber schon von Anfang an ein Format, mit dem man Text-Ressourcen sehr viel leichter erfassen und verwalten kann. Dabei handelt es sich um einfache Text-Dateien mit folgendem Aufbau:

; Kommentare beginnen mit ; oder #
; Texte werden als Name/Wertepaare aufgeschrieben

Bezeichner1 = Das ist ein Text.
Bezeichner2 = Das ist ein anderer Text.

Das ist schon alles, was man zum Aufbau dieser Dateien wissen muss. Bis einschließlich Visual Studio 2005 hatten diese Dateien einfach die Endung .txt und mussten mit dem SDK Tool resgen mehr oder weniger manuell in .resources Dateien konvertiert werden. Im Gegensatz dazu wurden .resx Dateien immer schon automatisch in .resources Dateien umgewandelt. Die .resources Dateien enthalten die Ressourcen in dem internen Format, wie sie in Assemblies eingebettet werden.

Seit Visual Studio 2008 kann man die Endung .restext anstatt .txt verwenden. Wenn man dann noch die Build Action auf Embedded Resource stellt, wird automatisch mit resgen eine entsprechende .resources Datei erzeugt und eingebettet.

Der Zugriff auf die Texte erfolgt wie bei .resx Dateien über die Klasse System.Resources.ResourceManager. Verschiedene Sprachen werden über den Dateinamen unterschieden. Beispielsweise enthält die Datei MyTextStrings .restext die neutrale bzw. default Sprache. Die Dateien MyTextStrings.de.restext und MyTextStrings.fr.restext enthalten die deutschen bzw. französischen Übersetzungen, die Datei MyTextStrings.de-CH.restext die schweizerischen Texte, die von denen aus MyTextStrings.de.restext abweichen usw. Der ResourceManager sucht abhängig von den UI-Einstellungen im laufenden Programm automatisch den richtigen Text heraus.

Der große Vorteil der .restext Dateien liegt in ihrem einfachen Format. Im Gegensatz zu dem für einfache Texte viel zu unübersichtlichen XML-Format der .resx Dateien kann man .restext Dateien ohne Weiteres einem Übersetzer geben. Dieser sieht sofort, was für ihn zu tun ist, wenn er die Datei in einem schlichten Texteditor öffnet. Als Einziges muss beachtet werden, dass die Datei im UTF-8 Format gespeichert werden muss, da resgen Unicode erwartet.

Hier geht's zum nächsten Tipp.

Tags:

.net | Silverlight | WPF

Stimmen zur dotnet Cologne 2010

by St. Lange 1. June 2010 23:10

Am 28.05.2010 fand die dotnet Cologne 2010 statt. Es hat sehr viel Spaß gemacht und wir haben von den Teilnehmern, Sprechern und Sponsoren ein durchgehend sehr positives Feedback erhalten. An dieser Stelle daher noch einmal ein großes Dankeschön an alle Beteiligten, die Ihr zum guten Gelingen der Veranstaltung beigetragen habt.

Als einer der Veranstalter möchte ich aber gar nichts weiter dazu sagen, sondern einfach auf das Echo im Web verweisen:

Dr. Joachim Fuchs: Gelungenes Wachstum

Dariusz Parys: Dariusz quatscht

Jürgen Gutsch: dotnet Cologne 2010

Gordon Breuer: dotnet Cologne 2010: Ein Rückblick

Thomas Bandt: Review: dotnet Cologne 2010

Jan Welker Ein perfekter Tag in der .NET Community

Oliver Sturm: Slides and samples from dotnet Cologne 2010

Martin Hey: Nachlese zur dotnet Cologne 2010

André Krämer: dotnet Cologne 2010 - was ein riesen Spaß

Michael Hülskötter: dotnet Cologne 2010, ein voller Erfolg, dotnet Cologne 2010, ein voller Erfolg – Teil 2]

Albert Weinert: Schreibt über die dotnet Cologne 2010, stürmt dotnet-kicks.de und gewinnt ein Tekpub Abo

Roland Weigelt: Das war die dotnet Cologne 2010

Mathias Raacke: dotnet Cologne 2010

Britt King: dotnet Cologne 2010 Winners, Typemock Isolator for Brownfield Projects

Thomas Mentzel: dotnet Cologne Tagebuch

Sergey Shishkin: DotNet Cologne 2010: WCF4 Live Coding with GitHub

Tags:

.net

dotnet Cologne 2010

by St. Lange 30. April 2010 18:00

Ab heute sind es noch 4 Wochen bis zur dotnet Cologne 2010, der Community Konferenz zum Launch von Visual Studio 2010 und .NET Framework 4, veranstaltet von der .NET User Group Köln und Bonn-to-Code.net.

Die Konferenz ist praktisch ausgebucht und die Sprecher und Sessions stehen fest, aber trotzdem ist für Albert, Roland und mich noch jede Menge zu tun. Mit rund doppelt so vielen Teilnehmern und Sessions wie die dotnet Cologne 2009 hat die Veranstaltung nun eine Größe erreicht, die gerade noch so als Community Konferenz organisiert werden kann. In diesem Jahr haben wir zum ersten Mal ein Hotel als Veranstaltungsort gewählt, was jedoch nur durch die zahlreichen Sponsoren und die ehrenamtlichen Sprecher möglich geworden ist.

Bei den Sessions haben wir uns viel Mühe gegeben, aus den zahlreichen eingegangenen Vorschlägen einen guten Querschnitt der Themen rund um Visual Studio 2010 und .NET 4 zu bieten. Ich selbst werde in meiner Session über Silverlight 4 sprechen und die interessantesten Neuerungen vorstellen.

Im Anschluss an die Konferenz findet noch der Grill-Abend des dotnet Forums statt. Die Teilnehmer des Forums können sich ab heute dafür anmelden.

Tags:

.net

SmartBackgroundWorker

by St. Lange 9. January 2010 13:01

Eine alternative Implementierung der Klasse BackgroundWorker erlaubt es, in ein und derselben Funktion beliebig zwischen UI- und Worker-Thread hin und her zu wechseln. Die verblüffende Implementierung führt zu sehr übersichtlichem Code ganz ohne Callbacks und Delegates.

Schon seit .NET 1.0 leistet die Klasse System.ComponentModel.BackgroundWorker gute Dienste, wenn es darum geht, zeitintensive Funktionen von einem Hintergrund-Thread ausführen zu lassen, damit das Userinterface nicht vorübergehend eingefroren wird. Über die drei Events DoWork, ProgressChanged und RunWorkerCompleted kann man sich über den Fortschritt der Ausführung informieren lassen. So weit, so gut. Wird die Aufgabenstellung komplizierter, beispielsweise weil man während der Hintergrundausführung die Oberfläche mit den bisher berechnenden Zwischenergebnissen aktualisieren oder mehrere Aufgaben hintereinander ausführen möchte und dabei ggf. Fehlermeldungen anzeigen möchte, wird der benötigte Code schnell recht unübersichtlich. Das liegt daran, dass man bei den meisten nicht trivialen Problemen immer eine einfache State Machine (einen Endlichen Automaten) bauen muss, welche(r) die Aktionen des UI-Threads und des Worker-Threads synchronisiert. Die benötigten Delegates, Lambda-Expressions und Synchronization-Contexte führen schnell zu schwer lesbarem und somit auch schwer wartbarem Code.

Wäre es nicht schön, wenn man ganz einfach sequenziellen Code und einfache Schleifen in einer übersichtlichen Funktion „einfach so“ hinschreiben könnte und dabei für einzelne Codeabschnitte jeweils angeben könnte, ob sie im UI-Thread oder im Background-Thread ausgeführt werden sollen?  Wäre nett, geht aber nicht, hätte ich gesagt, bevor ich letztes Jahr den Kurzvortrag von Ralf Hoffmann bei einem Treffen der Bonner .NET Usergroup gesehen habe. Ralf verwendet den yield-Befehl für eine erstaunliche Lösung des beschriebenen Problems. Da Ralf keinen Blog betreibt, der Ansatz aber sehr nützlich ist, möchte ich seine Idee hier vorstellen.

Beispielanwendung sdddd

Zur Demonstration des Verfahrens nehmen wi xxxr an, wir wollen irgendwelche Items berechnen und eine Listbox damit füllen. Das Berechnen eines jeden Items dauert so lange, dass es in einem Background-Thread ausgeführt werden muss. Jedes Item soll jedoch sofort angezeigt werden, wenn es berechnet wurde und nicht erst am Ende des Vorgangs, wenn alle Ergebnisse vorliegen. Außerdem soll die Berechnung über das UI abgebrochen werden können, wenn es dem Anwender zu lange dauert.

Hier das Beispiel als Silverlight-Anwendung. Die Berechnung jedes Items dauert eine knappe Sekunde und lastet dabei den Worker-Thread voll aus, wie ein Blick in den Task-Manager zeigen kann. Der Browser und die Silverlight-Anwendung werden davon aber nicht blockiert. Der Vorgang kann jederzeit über einen Button abgebrochen und wieder neu gestartet werden.

Die zugehörige Worker-Funktion aus der Codebehind-Datei sieht wie folgt aus:

/// <summary>
/// A simple function that gets alternating executed by
/// the UI-thread and a background-thread.
/// </summary>
IEnumerator<SwitchTo> MyWorkerFunktion(string someParameter, int someOtherParameter)
{
  // At start we run in the background thread, so switch to UI-thread
  // to update the StartStopButton and clear the ListBox
  yield return SwitchTo.UIThread;
  StartStopButton.Content = "Stop";
  Listbox.Items.Clear();

  // Now switch to background thread and start the loop
  yield return SwitchTo.BackgroundThread;
  for (int idx = 1; idx <= 25; idx++)
  {
    // Get the next item
    string item = DoSomeHardWorkToGetAnItem(idx);

    // Switch to UI-thread to update the list-box
    yield return SwitchTo.UIThread;
    Listbox.Items.Add(item);

    // Check if user pressed the Stop button
    if (_stop)
    {
      Listbox.Items.Add("<Operation was canceled by user>");
      _stop = false;
      break;
    }

    // Switch back to background thread for next loop iteration
    yield return SwitchTo.BackgroundThread;
  }

  // We have done and reset Start/Stop button in UI-thread
  yield return SwitchTo.UIThread;
  StartStopButton.Content = "Start";
}

Unglaublich, oder? Über den Enum-Typ SwitchTo wird im yield return angegeben, ob der nächste Codeabschnitt im UI- oder im Background-Thread ausgeführt werden soll.  Der Code ist praktisch selbstdokumentierend und sehr gut lesbar.

Aufgerufen wird diese Funktion beim Drücken des Start-Buttons wie folgt:

_smartBackgroundWorker.RunWorkerAsync(MyWorkerFunktion("Hello", 42));

Die Parameter der Woker-Funktion werden im Beispiel nicht gebraucht und sollen nur andeuten, dass man ohne weiteres beliebige Parameter übergeben kann.

Warum funktioniert das?

Um den SmartBackgroundWorker zu verwenden, muss man nicht in allen Einzelheiten verstehen, warum bzw. wie er funktioniert. Für Interessierte hier eine knappe Zusammenfassung der internen Struktur.

Der in C# 2 eingeführte yield-Befehl kann in Funktionen verwendet werden, die IEnumerator als Rückgabewert haben. Zu einer solchen Funktion generiert der Compiler eine passende private Klasse, die u.a. von IEnumerator  abgeleitet ist und daher auch die Funktion MoveNext implementieren muss. Der ursprüngliche Code mit den yield return-Anweisungen wird nun vom Compiler in eine State Machine konvertiert, die beim Aufruf von MoveNext jeweils in den nächsten Zustand übergeht. Als Effekt davon führt ein Durchlaufen des Iterators mit MoveNext dazu, dass jeweils genau die Statements bis zum nächsten (ursprünglichen) yield return ausgeführt werden (bzw. der Durchlauf beendet wird). Der generierte Code von MoveNext kann dabei sehr umfangreich und verworren werden, wie ein Blick darauf mit dem .NET Reflector zeigt. Diese Komplexität stört aber nicht weiter, da der Code ja vom Compiler unsichtbar und zuverlässig generiert wird.

Durch den Einsatz von yield return haben wir also erreicht, dass der Code unserer Worker-Funktion abschnittsweise ausgeführt wird. Wir müssen nun nur noch dafür sorgen, dass zwischen den einzelnen Aufrufen ggf. der ausführende Thread gewechselt wird. Dies wird innerhalb von SmartBackgroundWorker in der Funktion OnRun durchgeführt.

/// <summary>
/// Steps through the worker function.
/// </summary>
void OnRun(object argument)
{
  try
  {
    SwitchTo context = SwitchTo.BackgroundThread;
    var enumerator = (IEnumerator<SwitchTo>)argument;
    bool moveNext = true;
    SendOrPostCallback nextStep = obj =>
                                    {
                                      moveNext = enumerator.MoveNext();
                                      if (moveNext)
                                        context = enumerator.Current;
                                    };
    while (moveNext)
    {
      if (context == SwitchTo.UIThread)
      {
        // Run next step synchronously on UI thread
        _uiSynchronizationContext.Send(nextStep, null);
      }
      else
      {
        // Run next step on background thread
        nextStep(null);
      }
    }
  }
  finally
  {
    _isRunning = false;
  }
}

Ein SmartBackgroundWorker merkt sich im Constructor den SynchronizationContext des UI-Threads und muss daher immer im UI-Thread angelegt werden. Beim Start der Worker-Funktion über RunWorkerAsync wird diese über ein Delegate an einen Thread aus dem ThreadPool gebunden. Bis hierher ist die Implementierung identisch mit dem .NET Typ BackgroundWorker. Der Thread aus dem Pool startet dann in obigem OnRun, die den Unterschied zu BackgroundWorker ausmacht.

In der Variablen nextStep wird der Code zum Durchlaufen des nächsten Iterator-Schritts gespeichert. Die Variable context speichert den Rückgabewert von yield return, also entweder UIThread oder BackgroundThread.

In der while-Schleife wird über den Wert von context unterschieden, in welchem Thread der nächste Codeabschnitt aufgerufen werden soll. Ist es der Background-Thread, wird nextStep direkt aufgerufen, denn OnRun läuft ja bereits im Worker-Thread. Ist es der UI-Thread, wird nextStep mit der Funktion Send von SynchronizationContext an den UI-Thread „gesendet“. Technisch passiert dabei folgendes: Durch Send wird eine Art „spezielles Event“ in die Event-Queue des UI-Threads eingereiht. Ist dieses Event an der Reihe, wird der Code in nextStep durch den UI-Thread ausgeführt und somit auch der nächste Abschnitt in unserer Worker-Funktion. Bis diese Ausführung abgeschlossen ist, blockiert der Background-Thread, d.h. der Funktionsaufruf von Send kommt erst dann zurück, wenn nextStep vom UI-Thread vollständig ausgeführt wurde.

Mit etwas Abstand betrachtet liegt also die Innovation von SmartBackgroundWorker in Folgendem: Anstatt einen normalen BackgroundWorker zu verwenden und selber eine State Machine zu bauen, die zwischen dem UI-Thread und einem Background-Thread jongliert, schreibt man relativ linearen Code zusammen mit yield return und lässt den Compiler die passende State Machine generieren.

Coole Sache. Wie schon gesagt, ich habe es nicht erfunden, sondern meine Implementierung nur aus Code von Ralf Hoffmann abgeleitet, der wiederum von einem Screencast von Jeffrey Richter inspiriert wurde (vermutlich diesem hier).

Bewertung

Der Einsatz von SmartBackgroundWorker ist nicht auf Silverlight beschränkt, sondern funktioniert mit WPF oder WinForms genauso. Ich habe nur deshalb Silverlight verwendet, damit ich die Demo direkt in diesen Artikel im Blog einbauen kann.

Im letzten Jahr haben wir den SmartBackgroundWorker in verschiedenen Silverlight-Anwendungen verwendet und er hat sich als sehr nützlich erwiesen. Hier meine persönliche pro/contra-Liste.

Vorteile

  • Leicht zu verwenden
  • Keine zusätzlichen Eventhandler, Delegates oder Lambdas notwendig
  • Führt zu übersichtlichem und intuitiv nachvollziehbarem Code
  • Worker-Funktionen lassen sich sehr leicht verketten bzw. ineinander verschachteln

Nachteile

  • Exception-Handling in der Worker-Funktion kann unübersichtlich werden, da yield nicht innerhalb von try/catch verwendet werden kann
  • In VB.NET nicht verwendbar, da diese Sprache nicht über ein yield-Statement verfügt (was aber eher eine Einschränkung der Sprache ist)

Insgesamt kann ich den SmartBackgroundWorker sehr empfehlen.

Fazit

Man kann sich natürlich fragen, ob das Ganze wirklich eine gute Idee oder nur ein Hack ist, da ja der yield-Befehl für etwas missbraucht wird, für das er nicht erfunden wurde. Meiner Meinung nach ist es eine sehr gute Idee, denn es ist eine saubere Lösung für ein konkretes Problem. Und dies ist das Einzige, worauf es letztlich ankommt, wenn man den Nutzen einer Idee bewerten will. Und im Vergleich zu anderen Innovationen finde ich die Schreibweise der Worker-Funktion sogar sehr elegant. Die Parallel-Extensions von .NET 4.0 sind beispielsweise auch sehr nützlich und innovativ, aber der Code, den man teilweise schreiben muss, ist doch arg gewöhnungsbedürftig. Aber dies ist ein anderes Thema.

Hier der Quellcode zum Downloaden:

SmartBackgroundWorker.zip (7,77 kB)

Tags:

.net | Silverlight | WPF

Powered by BlogEngine.NET 1.6.1.0 - Impressum