Asynchrone Programmierung mit Threads Teil 1
In diesem ersten Teil dieser Serie möchte ich auf die Theorie hinter der Asynchronen Programmierung zu Sprechen kommen, Vor- und Nachteile dieser und auf Probleme, die dabei entstehen können.
Bild 1: Mooresches Gesetz grafisch dargestellt (klicken zum vergrößern) Quelle: Wikipedia
Wenn man mal auf die letzten Jahre schaut, sieht man eine starke Entwicklung der Computerrechenpower, zB. 1998 galt ein Computer mit 500 MHz noch als sehr leistungsfähig. Bis heute gilt auch noch (naja zumindest fast) das Mooresche Gesetz, welches besagt, dass alle 2 Jahre sich die Schaltkreise auf einem Chip (CPU) verdoppeln und somit auch die Leistung.
Da die Rechenleistung der Computer stetig stieg, war es für Programmierer meist nicht nötig, bzw. gab es keine Anreize die Software effizienter zu gestalten, denn mit jedem neuen Computer lief auch das Programm schneller!
Doch heute sind wir an einer Grenze angekommen, an der es kaum noch möglich ist eine einzelne CPU leistungsfähiger zu machen, und somit stagnieren wir zur Zeit bei ca 3 - 4 GHz bei SongleCore CPUs. (Ich selbst habe vor ca 6 Jahren geschätzt, dass die Marke von 5 GHz nicht mehr überschritten wird.)
Da man die Transistorenzahl auf den Chips nicht mehr erhöhen kann bedient sich die Industrie einem Trick. Sie bauen einfach zwei (oder mehr) CPUs in eine. Das ganze nennt sich dann DualCore. Man hat also zwei echte von einander unabhängig arbeitende CPUs.
Bild 2: Im Windows Taskmanager sieht man die 2 CPUs bei der Arbeit (oben)
Das Problem hierbei ist nur, dass man nun zwar zwei Kerne hat, diese aber nicht schneller geworden sind! So hat man zB. 2x 2,2 GHz was aber nicht heißt, dass die Rechenleistung dieser CPU bei 4,4 Ghz liegt!
Denn ein Programm läuft standardmäßig nur auf einem Kern, und da dieser nicht schneller geworden ist läuft das Programm darauf genauso schnell wie auf einem SingleCore 2,2 GHz Prozessor! (Natürlich ist das nicht ganz wahr, sicher läuft das Programm auf dem DualCore etwas schneller, da das System andere Prozesse auf den zweiten Kern auslagert, aber der Performancegewinn ist nicht enorm!)
Also sind wir Programmierer nun gezwungen unsere Software so zu gestalten, dass diese möglichst alle Kerne der CPU auslasten. Also Parallelprogrammierung anwenden!
Doch das ist leider leichter gesagt als getan, denn es gibt viele Steine die da in dem Weg liegen und viele Kleinigkeiten die man beachten muss! Der Anfang ist aber verschiedene Abläufe des Programms in einzelne Threads aufzuteilen.
Zunächsteinmal muss man wissen, dass nicht jedes Programm in Threads aufgeteilt werden kann (dazu gleich mehr) und desweiteren muss man verstehen, dass egal wieviele Prozessoren ein Computer hat man ein Programm nicht unendlich schnell machen kann!
Daher ist die Aussage "je mehr Kerne desto schneller das Programm" falsch! Warum das so ist werde ich nun erklären!
Sehen wir uns eine vereinfachte Programmkette an:
Darstellung 1: Sequentieller Programmablauf
Hier steht "T" für Thread und "P" für Prozedur.
Da jedes Programm einen klaren Einstiegspunkt haben muss (was hier P1 ist) kann dieser schon mal nicht in einen zweiten Thread ausgelagert werden. Der Endpunkt der meist alle Ressourcen freigibt ist der Punkt wo alles zusammenläuft, daher kann dieser auch nicht ausgelagert/aufgeteilt werden!
Somit können schon 2 von unseren 7 Prozeduren nicht ausgelagert werden und müssen sequentiell (nacheinander) ablaufen. Gut, schauen wir uns mal die restlichen 5 Prozeduren an.
Ein Programm kommt nicht ohne sequentielle Abläufe aus, da es oft so ist, dass eine Prozedur mit Ergebnissen einer anderen Prozedur arbeitet. Logischerweise können diese zwei Prozeduren nicht parallel laufen.
Für dieses Beispiel nehmen wir an, dass jeder Thread auf einer eigenen CPU laufen würde!
Für unser Beispiel denken wir uns mal, dass P4 mit Ergebnissen von P3 arbeitet und diese daher nicht parallelisiert werden können. Somit fallen wieder 2 Prozeduren aus, die man parallelisieren könnte.
Desweiteren benötigt P6 die Daten aller Prozeduren die davor liefen und kann daher auch nicht ausgelagert werden!
Somit bleiben uns zwei Prozeduren, die wir in einen andren Thread packen könnten. Was ca. so aussehen würde:
Darstellung 2: Asynchroner Programmablauf mit 2 Threads
Wie man sieht konnten wir in diesem Programm P2 und P5 erfolgreich in einen zweiten Thread auslagern. Doch was heißt das für unser Programm? hier ein kleines Rechenbeispiel:
Nehmen wir an, dass jede Prozedur eine Laufzeit von 5 Sekunden hat, das würde bedeuten, dass unser Programm wie in Darstellung 1 aufgeführt 35 Sekunden benötigen würde, um durch zulaufen.
Da bei dem Programm aus Darstellung 2, 2 Prozeduren "Parallel" ablaufen, verringert sich die Zeit auf 25 Sekunden. Also eine Ersparnis von knapp 30 %!
Natürlich ist das hier eine Ausnahme, denn es wird wohl kein Programm geben, dessen Prozeduren alle Gleichschnell sind, was uns zu unserem Nächsten Problem führt!
Denn wenn die Prozeduren nicht alle gleich schnell sind kann es zu Verzögerungen kommen! Das ist so, als wenn man Einen Tisch bauen will, und bestellt die Tischbeine bei Amazon und die Tischplatte bei Ebay. Den Tisch wirklich zusammenbauen kann man erst wenn beide Teile da sind! Fehlt eins der Teile muss man warten bis das andere auch ankommt!
Wenn wir das nun auf unser Programm aus Darstellung 2 beziehen heißt das, P6 erst weiter machen kann, wenn sowohl P4 als auch P5 Abgeschlossen sind! Wenn P4 zB. schneller fertig ist dann pausiert der erste Thread solange bis der zweite fertig ist! Dies kann zu unangenehmen Effekten führen, die das Programm sogar verlangsamen können statt es zu beschleunigen!
Wenn P2 und P5 also 6 Sekunden benötigen würden statt 5 würde die Laufzeit unseres Programms sich um 2 Sekunden verlängern also 27!
Nun könnte man aber sagen, P2 und P5 sind ja nicht von einander abhängig also lagere ich diese zwei Prozeduren auch noch aus:
Darstellung 3: Asynchroner Programmablauf mit 3 Threads
Hier müsste P6 auf die Fertigstellung von P4, P2 und P5 warten!
Das würde allerdings nur Sinn machen, wenn die Prozeduren P2 und P5 in der Summe länger brauchen würden als P3 und P4! Denn wenn P2 und P5 zusammen schneller sind als als P3 und P4 würde dieses erneute Auslagern nichts bringen, da sie sowieso schneller fertig wären als P3 und P4!
Wie man sieht wurde unser Programm durch die Auslagerunggen von Prozeduren um fast ein Drittel schneller! Aber man sieht auch, dass unser Programm auch nicht schneller werden würde wenn man 10 oder 100 CPUs hätte! Da wir sowieso nur 2 CPUs benutzen bringen uns die restlichen CPUs nichts da wir unser Programm nicht weiter aufteilen können!
Besonders Sinn macht es Prozeduren auszulagern, die viel Zeit in Anspruch nehmen. Denn wenn man solche Prozeduren Sequentiell abarbeitet blockieren diese den Ganzen Ablauf! Besonders Schleifen machen das ganz gerne!
Wenn man zb. eine Grafische Oberfläche hat und eine lange Prozedur im selben Thread (der auch die GUI verwaltet) startet, reagiert diese solange nicht mehr bis diese Prozedur durchgelaufen ist! Dann sieht man meist folgendes:
Bild 3: Man beachte das "Keine rückmeldung"
Um das zu verhindern sollte man längere Schleifen und Rechenoperationen immer in einen Hintergrund Thread verschieben damit die Oberfläche weiterhin bedienbar bleibt!
Ansich ist Threading sehr nützlich wir haben nun einige Vorteile kennengelernt wie den Performancegewinn, aber auch Nachteile gefunden, wie etwa Verzögerungen. Aber auch bei der Programmierung von Threads gibts einige Gefahren die man nur durch genaues Debuggen finden kann!
An dieser Stelle möchte ich ein Beispiel bringen, welches die Problematik darstellt!
Nehmen wir an, wir haben ein Programm, dass für ein Ehepaar das Konto verwaltet! Jeder der beiden hat dabei seinen eigenen Thread, der für den Ehepartner das Geld vom Konto abhebt! Die Hauptaufgabe unseres Programms ist es dafür zu sorgen, dass man das Konto nicht überbelastet also mehr abhebt als drauf ist!
Ein typischer Programmablauf wäre dann wie folgt:
### Programmstart Verbinde mit Konto Frage wieviel Geld abgehoben werden soll speichere den gewünschten Betrag in Variable $Betrag Prüfe ob $Betrag kleiner ist als der Betrag auf dem Konto Wenn genug Geld auf dem Konto vorhanden ist Hebe $Betrag vom Konto ab Trenne Verbindung ### Programmende
Beide Threads (von beiden Ehepartnern) laufen dabei Parallel, damit der eine nicht waren muss und immer sofort Zugriff hat.
Auf den ersten Blick sieht das Programm sicher aus, man prüft ob genug Geld auf dem Konto ist bevor was abgehoben wird. Totsicher - oder?
Eigentlich ja, wenn das Programm nur in einem Thread laufen würde! Denn es könnte ja zu dem Fall kommen, dass beide Ehepartner gleichzeitig Geld abheben wollen:
Nehmen wir an auf dem Konto sind 100 Euro!
Dann würde folgendes passieren: Beide wollen 70 Euro abheben, nun würden die Threads "gleichzeitig" prüfen, ob genug Geld auf dem Konto ist. Und da sie beide "gleichzeitig" prüfen und noch keiner Geld abgehoben hat ist die Bedingung erfüllt und beide Threads heben 70 Euro vom Konto ab, da sie ja vorher geprüft haben, ob genug Geld da war - und es war ja auch genug da!
Als Ergebnis hätten wir einen neuen Kontostand von -40 Euro - genau das was wir mit dem Programm verhindern wollten!
Ähnliche Probleme können auftreten, wenn mehrere Threads mit mit der selben Datenquelle arbeiten. Wenn zB. Thread 1 eine Datei löscht auf die Thread 2 noch zugreifen will, stürzt das Programm mit einer Exception ab!
Man müss also Möglichkeiten suchen solche Probleme zu erkennen und vorzubeugen! Das werde ich dann im Teil 2 dieser Serie an Hand von ein paar Beispielen behandeln!
.NET: Status einer TreeView Speichern und Laden
Wenn man mit TreeView Steuerelementen arbeitet kommt man irgendwann nicht drumherum diese zu speichern. Dieses Thema habe ich ja schon in 2 Beiträgen besprochen und eine Klasse zur Verfügung gestellt, welche die TreeView in einer XML Datei speichert.
Das Problem ist nur, wenn ich den Inhalt der TreeView wieder geladen habe möchte ich normalerweise auch, dass die Ordner, die beim Schließen des Programms geöffnet waren auch nach dem Laden wieder offen sind. Das spart eine menge Zeit und ist einfach bequemer.
Bild 1: Das Demoprojekt nach dem Laden des Treenode-Zustands
Leider bietet uns die TreeView Klasse keine Möglichkeit dies schnell und einfach zu lösen. Darum muss eine eigene Funktion her!
Ich bin diese Problemstellung folgendermaßen angegangen: Ich durchlaufe die komplette TreeView und schaue, welche Äßte geöffnet sind. Jedesmal wenn ich auf einen Ast stoße, der offen ist notiere ich mir eine "1" ansonsten eine "0".
Irgendwann habe ich dann eine Variable mit lauter Nullen und Einsen. Diese Speichere ich in einer Datei, die ich dann beim Laden des Programms wieder durchlaufe und die Knoten wieder öffne.
Das ganze sieht dann so aus: (Speichern)
Public Function saveNodeState(ByVal tn As TreeNode, Optional ByVal retv As String = "") As String
Dim retVal As String = retVal & retv
'Jede Node in der übergebenen Node durchgenen
For Each tnn As TreeNode In tn.Nodes
'Prüfen, welche Nodes geöffnet sind und diesen zustand speichern
' 1 => offen
' 0 => geschlossen
If tnn.Nodes.Count > 0 Then
If tnn.IsExpanded Then
retVal = retVal & "1"
Else
retVal = retVal & "0"
End If
'Hats der node weitere "kinder" ist es ein ordner
'also diese node rekursiv an sich selbst übergeben!
retVal = saveNodeState(tnn, retVal)
End If
Next
Return retVal
End Function
Und Laden:
Public Function loadNodeState(ByVal tn As TreeNode, Optional ByVal settings As String = "")
'Jede Node in der übergebenen Node durchgenen
'Den Zustand der Nodes wiederherstellen (Offen oder geschlossen)
For Each tnn As TreeNode In tn.Nodes
If tnn.Nodes.Count < 1 Then
'Wenn der Node keine Kinder hat überspringen
Else
'Prüfen, ob der Node früher geöffnet war
If settings <> "" Then
If settings.Substring(0, 1) = "1" Then
tnn.Expand()
End If
settings = settings.Substring(1, settings.Length - 1)
End If
'Hat der node weitere "kinder" ist es ein ordner
'also diesen node rekursiv an sich selbst übergeben!
settings = loadNodeState(tnn, settings)
End If
Next
Return settings
End Function
Für beide Funktionen ist ein rekursiver Aufruf von Nöten, da man auch die Unterordner durchlaufen muss! Das ganze ist im Prinzip sehr einfach und funktioniert wunderbar!
Wenn ihr auch (bessere) Ideen habt wie man das bewerkstelligen könnte bitte als Kommentar posten.
Dazu habe ich auch ein Kleines Demoprojekt angefertigt: Download
VB.NET: Bilder Hochladen mit der TwitPic API Teil 3
In dem dritten Teil der API-Reihe möchte ich euch zeigen wie man mit Hilfe der TwitPic API Bilder hochladen kann.
Diesesmal schreibt die API vor, dass man POST-Requests verwendet um der Seite Daten zu senden. Das macht die Sache etwas komplizierter, und dazu kommt noch, dass wir auch noch ein Bild verschicken müssen, also müssen auchnoch binäre Daten verschickt werden. Aber langsam, erstmal die Theorie!
Ihr kennt doch sicher die normalen Eingabemasken wie zB. die von imageshack.us. Diese besteht aus einem Dateiauswahlfeld, mehreren Checkboxen und einem Textfeld. Wer schonmal HTML gemacht hat wird wissen dass dieses Gebilde aus so genannten Interaktiven Elementen ein Formular darstellt welches nach dem Betätigen des Senden-Buttons an eine vorher definierte Adresse gesendet wird!
Dabei sendet der Browser einen HTTP POST-Request an den Server. Ein solcher Request besteht für gewöhnlich aus einem Kopf, den Daten und einem Fuß. So könnte zB. ein HTTP POST-Request aussehen:
POST /wiki/Spezial:Search HTTP/1.1 Host: de.wikipedia.org Content-Type: application/x-www-form-urlencoded Content-Length: 24 search=Katzen&amp;go=Artikel
Der Server verwertet diese Daten dann und schickt eine Antwort. Je nachdem wie wir angefragt haben bekommen wir als Antwort eine XML-Struktur (Wie in den Letzten Beispielen) oder eine HTML Struktur. Die Kommunikation geschieht dabei auf dem HTTP Port 80 und läuft über das TCP Protokoll!
In den letzten beiden Teilen haben wir jeweils so genannte GET-Requests erstellt, bei denen nur der URL-Strang mit den Variablen versandt wurde.
Da wir aber nun ein Bild übertragen wollen, müssen wir versuchen ein POST-Request-Objekt zu erstellen, das wir dann an den TwitPic Server schicken können.
Unser POST-Objekt muss dabei folgende Daten übermitteln:
- Unseren Twitter Benutzernamen
- Unser Twitter Passwort
- Das Bild das wir einstellen wollen
Wenn wir diese Daten nun in unser Objekt verfrachten, müssen wir dafür sorgen, dass der Server später weis, was was ist und die Daten von einander trennen kann. Deswegen müssen wir für jeden Eintrag einen eigenen Head und Body erzeugen.
Das Bild muss dann auch in das binäre Format umgewandelt werden und dann mit den anderen Daten durch ein Strom an den Server gesendet werden! - Hört sich kompliziert an, ich weis aber es hält sich in Grenzen!
Hier erstmal der Code für das Hochladen des Objekts mit allen Daten und der Abruf der Antwort. Tipp: Ihr könnt den Code besser lesen, wenn ihr auf den Knopf oben rechts in der Code Darstellung klickt!
Public Function uploadPic(ByVal pic As Byte(), ByVal filename As String, ByVal user As String, ByVal pass As String)
Dim encoding As String = "iso-8859-1"
'Erzeugen einer einzigartigen identifikation
Dim gui As String = Guid.NewGuid().ToString()
'Diese wird für den Header und den footer der Daten benötigt
Dim head As String = String.Format("--{0}", gui)
Dim foot As String = String.Format("--{0}--", gui)
'Einen Stringbuilder erstellen, in dem wir nun bequem die
'benötigten POST Daten speichern können
Dim contents As StringBuilder = New StringBuilder()
'Benutzerdaten schreiben (benutzername)
contents.AppendLine(head)
contents.AppendLine(String.Format("Content-Disposition: form-data; name=""{0}""", "username"))
contents.AppendLine()
contents.AppendLine(user)
'Benutzerdaten schreiben (passwort)
contents.AppendLine(head)
contents.AppendLine(String.Format("Content-Disposition: form-data; name=""{0}""", "password"))
contents.AppendLine()
contents.AppendLine(pass)
'Header schreiben
contents.AppendLine(head)
'Bildinformationen schreiben, Bildkopf und die Binärdaten
Dim fileHeader As String = String.Format("Content-Disposition: file; name=""{0}""; filename=""{1}""", "media", filename)
Dim fileData As String = System.Text.Encoding.GetEncoding(encoding).GetString(pic)
'Informationen zu dem Übergebenen Dateityp schreiben
contents.AppendLine(fileHeader)
contents.AppendLine(String.Format("Content-Type: {0}", "image/jpeg"))
contents.AppendLine()
contents.AppendLine(fileData)
'Durch schreiben des footers signalisieren dass keine Daten mehr kommen
contents.AppendLine(foot)
MessageBox.Show(contents.ToString)
'Stream Reader zum lesen der Antwort von Twitpic
Dim reader As StreamReader
Dim result As String
Dim response As HttpWebResponse
'Einen Webrequest zu der TwitPic API erstellen
Dim request As HttpWebRequest = WebRequest.Create("http://twitpic.com/api/upload")
'Die Auflagen die in der API beschreiben sind erfüllen:
'API-Zitat:
'---
'Fields to post in:
'(post data should be formatted as multipart/form-data)
'---
request.ContentType = String.Format("multipart/form-data; boundary={0}", gui)
request.Method = "POST"
'Die Daten die noch im Stringbuilder als String vorliegen
'in das byte (Binär)-Format umwandeln, damit die API diese annimt
Dim bytes As Byte() = System.Text.Encoding.GetEncoding(encoding).GetBytes(contents.ToString())
request.ContentLength = bytes.Length
'Einen Stream aus dem WebRequest erstellen
Dim writer As Stream = request.GetRequestStream()
'Die Binären Daten in den Strom schreiben
writer.Write(bytes, 0, bytes.Length)
response = request.GetResponse()
'Antwort von Twitpic empfangen
reader = New StreamReader(response.GetResponseStream)
result = reader.ReadToEnd
reader.Close()
Return result
End Function
Wenn ihr diesen Code ausführt, wird ein POST-Objekt erzeugt und an den TwitPic Server gesendet. Dabei werden diese Informationen übermittelt. (Bild 1)
Man sieht ganz klar die Aufteilung. Der Kopf der aus der UID und den Meta Daten besteht und der Body, der nur den Wert beinhaltet.
Das Bild das ich hier sende hat einen etwas größeren Kopf, scheint dafür aber einen sehr kleinen Body zu haben. Ich kann euch beruhigen, das ist nur so, weil Windows, oder das MessageBox Objekt keine Binäre Darstellung von Zeichen beherrscht.
Eigentlich kommt dadrunter noch ein Footer (Fuß), aber dieser wird wegen der binären Daten abgeschnitten!
Natürlich bekommt man auch eine Antwort, in diesem Fall eine im XML-Format. Diese beinhaltet entweder die Erfolgsmeldung oder eine Fehlermeldung mit einer Kurzen Beschreibung, woran es lag. An Bild 2 könnt ihr erkennen wie so ein Fehler aussehen könnte!
Wenn kein Fehler kommt steht dort dementsprechend die URL zum Bild statt der Fehlermeldung!
Wenn ihr nicht direkt den Code versteht ist das nicht schlimm. Man sollte ersteinmal wissen was der Browser da bei jedem Klick eigentlich hin- und herschickt. Dazu kann ich den Wikipedia-Artikel empfehlen. Es schadet auch nicht, weitere Beispiele anzuschauen bei denen mit HTTPWebRequest gearbeitet wird. ZB Hier oder Hier. (leider alles in C#)
Zum Schluss sei hier noch gesagt, dass die TwitPic API noch weitere Möglichkeiten bietet, wie zB. das Anfertigen von Vorschaubildern oder das Anhängen von Tweets an das Bild sodass automatisch auch euer Twitter Status mit dem neuen Bild aktualisiert wird!
Bild 3: Das Demoprojekt in Aktion
Wie immer könnt ihr euch auch das Demoprojekt herunterladen: Download
Ich hoffe das Beispiel hat euch gefallen und freue mich auf euer Feedback!
VB.NET: Kurze URLs erstellen mit bit.ly Teil 2
Wie bereits im letzten Beitrag angekündigt möchte ich in diesem Beispiel zeigen, wie man seine URLs mit dem Dienst bit.ly verkürzen kann.
Ersteinmal die Frage "warum überhaupt bit.ly? Ich habe doch tinyurl.com!"
Nun das ist einfach, denn bit.ly bietet einen größeren Featureumfang. Dawäre:
- Eigene Accounts
- Linkmonitoring (wie oft wurde mein Link geklickt)
- Statistiken
- Erweiterte Abfragen und Parameter
- Kürzerer Name (bit.ly = 6 Zeichen | tinyurl.com = 11 Zeichen)
Allgemein kann man sagen, bit.ly ist professioneller aufgelegt! Aber mit einem höherem Featureumfang wird die Benutzung der APIs auch etwas komplexer.
An die API-Dokumentation von bit.ly kommt man sehr einfach in dem man auf deren Homepage ganz unten auf "API" klickt und dann dem gezeigten Link folgt. Hier findet man eine Komplette Dokumentation aller Möglichkeiten.
Was direkt auffällt ist, dass es viele verschiedene Funktionen gibt und die URL die wir benutzen müssen viel länger ist.
Das was aber auch sehr wichtig ist, ist dass man diesen Dienst nicht anonym benutzen kann, sondern sich einen Account erstellen muss um einen Sogenannten API-Key zu erhalten. Das ist hauptsächlich dafür da, um Missbrauch der Seite zu verhindern (zB. Spam).
Die Registrierung geht sehr schnell nach der Eingabe von Benutzername, E-Mail und Passwort. Wenn mann dann eingeloggt ist muss man folgende Seite aufrufen: http://bit.ly/account/your_api_key/ hier findet man seinen API-Schlüssel, den man unbedingt braucht, um URLs zu generieren, also aufschreiben!
ACHTUNG: In meinem Beispiel hier verwende ich den Demo-API-Schlüssel von bit.ly, der auch in der Doku steht. Wenn ihr was eigenes macht müsst ihr euch unbedingt registrieren!
Hat man das einmal geschafft kann man mit dem Programmieren loslegen:
Public Function shortURL(ByVal langeURL As String)
Dim retVal As String = ""
'Für dem API Aufruf wird benötigt:
'-> Die Versionsnummer (version)
'-> ein Benutzername (login)
'-> ein API-Schlüssel (apiKey)
'Den API-Aufruf starten:
Dim myRequest As WebRequest = WebRequest.Create _
("http://api.bit.ly/shorten?" & _
"version=2.0.1" & _
"&format=xml" & _
"&longUrl=" & langeURL & _
"&login=bitlyapidemo" & _
"&apiKey=R_0da49e0a9118ff35f52f629d2d71bf07")
Dim myResponse As WebResponse = myRequest.GetResponse
'Den erzeugen Strom auslesen
Dim myReader As StreamReader = _
New StreamReader(myResponse.GetResponseStream)
'Stream auslesen
retVal = myReader.ReadToEnd
myReader.Close()
Return retVal
End Function
Wie man sieht hat sich unser Code kaum verändert! Ich habe hier nur dem Link etwas angepasst, und um folgende Punkte erweitert:
- Version (Die Version der API, muss laut Doku mit angegeben werden!)
- Format (Gibt das Format an, das für die Antwort verwendet werden soll)
- Login (Der Benutzername von unserem Account)
- APIKey (Der API-Schlüssel, den wir erhalten haben)
- longURL (Und die zu kürzende URL)
Was hier wichtig ist, ist das Format. Hier hat man die Wahl zwischen XML und "json", wobei ich euch XML empfehlen würde, da es einfach übersichtlicher und einfacher verwendbar ist!
Wenn wir nun einen Request mit diesem langen Link erstellen erhalten wir auch eine Antwort im XML oder json Format:
Bild 1: Die zwei Rückgabetypen von bit.ly
Wie man sieht erhalten wir hier anders als bei tinyurl.com nicht direkt die kurze URL, sondern einen Haufen Informationen, wie Fehlercode, Fehlernachricht etc...
Uns interessiert aber natürlich nur die neue kurze URL, also müssen wir diese ersteinmal aus dem XML Durcheinander auslesen. Dazu habe ich diese Funktion erstellt:
Public Function extractURL(ByVal response As String)
'Ein XML-Dokument anhand der Antword von bit.ly erstellen:
Dim myDoc As New XmlDocument
myDoc.LoadXml(response)
'Nun nehmen wir uns das Element raus, in dem die
'neue kurze URL steht:
Return myDoc.SelectSingleNode("/bitly/results/nodeKeyVal/shortUrl").InnerText
End Function
Hier wird einfach nur ein XML-Dokument erzeugt (aus der Antwort) und dann der entsprechende Node ausgelesen!
Ein Hinweis noch: In der Doku wird weiterhin erwähnt, dass man nur ein bengenztes Kontingent an API-Aufrufen hat. Wenn man die API viel nutzen will sollte man sich bei bit.ly melden!
Auch hierzu habe ich ein Kleines Demoprogramm angefertigt:
Bild 2: Der uRL-Kürzer in Aktion
Download Hier: Download
Hat euch der Beitrag gefallen? Ich freu mich auf euer Feedback!
VB.NET/PHP: Kurze URLs erstellen mit Tinyurl Teil 1
Sie sind sehr beliebt und es werden immer mehr! Sogenannte URL-Shorter wie Tinyurl.com, bit.ly oder wie sie alle heißen. Diese kleinen Dienste verkürzen beliebig lange URLs in kleine handliche "URLchen".
Dienste wie Twitter, bei denen es auf die Länge der Beiträge ankommt können ohne diese Dienste nicht mehr leben, weswegen Twitter sogar die Nutzung von bit.ly vorschreibt!
Bild 1: Ein ICQ 6 Chatfenster mit einer E-BAy-URL die nun einen Smiley enthält
Aber auch eigene Projekte, egal ob online oder offline können so etwas gut gebrauchen. Denn wenn man zb eine E-Bay URL weiter geben möchte ist diese nicht selten mehrere Zeilen lang und wird, je nach Programm falsch dargestellt. Plötzlich enthalten die URLs Smileys die durch Kombinationen wie "=p" oder "8)" in der URL entstehen!
Das ist sehr unangenehm, da der Link dann auch nicht mehr angeklickt werden kann und man den Fehler erst wieder ausbügeln muss. Da erspart so ein Dienst natürlich viel Zeit!
Gut für uns, dass viele der Anbieter auch eine API für ihren Dienst anbieten. So können auch wir Gebrauch davon machen und Vorgänge automatisieren.
Wenn man bei Google nach "tinyurl API" sucht wird man sehr schnell fündig und erfährt, dass man einfach nur die URL "'http://tinyurl.com/api-create.php?url=" aufrufen muss und dann seine zulange URL dahinter packen kann.
Als Ergebnis kommt dann eine gekürzte Version raus!
Wie setzen wir das nun im Code um? Wir müssen ersteinmal auf die Internetseite zugreifen, und das Ergebnis herunterladen!
Das wars auch schon! Hier ist unsere Funktion:
Public Function shortURL(ByVal langeURL As String)
Dim retVal As String = ""
'Den API-Aufruf starten:
Dim myRequest As WebRequest = WebRequest.Create _
("http://tinyurl.com/api-create.php?url=" &amp;amp; langeURL)
Dim myResponse As WebResponse = myRequest.GetResponse
'Den erzeugen Strom auslesen
Dim myReader As StreamReader = _
New StreamReader(myResponse.GetResponseStream)
'Stream auslesen
retVal = myReader.ReadToEnd
myReader.Close()
Return retVal
End Function
Wie man sieht wird nichts anderes gemacht, als oben beschrieben!
Naja, man muss dazu sagen, dass tinyurl.com der wohl "einfachste" Anbieter ist, da dieser einfach nur die "nackte" URL zurück gibt. Deswegen habe ich das hier auch als Beispiel gewählt!
Noch einfacher lässt sich das Ganze in PHP ausdrücken:
//PHP Code:
function TinyURL($u){
return file_get_contents('http://tinyurl.com/api-create.php?url='.$u);
}
So kann man sehr angenehm seine langen URLs kürzen und seine Besucher erfreuen
Ich habe hierzu ein kleines Beispiel in VB.NET angefertigt, das ihr bei Interesse herunterladen könnt!
Bild 2: Das Demoprogramm in Aktion
Download Hier: Download
Wenn bei euch Interesse besteht stelle ich euch auch die anderen Anbieter wie zB. bit.ly vor und fertige ein Beispiel zu deren API an. Gebt einfach per Mail oder Kommentar bescheid!
.NET: Parallelprogrammierung
Heutzutage haben die meisten Computer und auch Laptops Prozessoren mit mehreren Kernen. Das wollen natürlich viele Programmierer für sich nutzen um ihre eigenen Programme zu beschleunigen.
Bild 1: Eine Intel CPU mit 4 Kernen
An dieser Stelle kommt das Stichwort "Multithreading" ins Spiel! Doch leider denken viele, dass wenn man mehrere Threads innerhalb eines Programms erstellt, diese dann auch auf verschiedenen Prozessoren ausgeführt werden. Doch dies ist leider nicht so!
Denn auf welchem Prozessorkern ein Thread läuft entscheidet der Thread-Sheduler des Systems, außer man sagt ihm was anderes. So werden alle Threads eines Programms meistens nur auf einem Prozessor ausgeführt und laufen somit nicht parallel sondern sequentiell!
Es sieht zwar aus, dass die beiden Threads gleichzeitig arbeiten, dem ist aber nicht so! Also ist multithreading nicht gleich parallel!
Denn nur wenn ein Programm parallel auf zwei Kernen läuft erzielt man auch einen Performance Gewinn, da erst dann die Threads wirklich parallel laufen! So kann auch Multithreading auf einem SingleCore CPU zu Performanceeinbußen kommen, da das erstellen der Threads und das Hin- und Herschlalten zwischen den Threads Zeit kostet!
Was dazu kommt ist, dass Parallelprogrammierung sehr komplex ist und es zu unvorhergesehenen Problemen kommen kann. Zum Beispiel können die so genannten "Dead Locks" entstehen, wenn zB. "Thread A" den Wert einer Varibale löscht, die "Thread B" aber noch benötigt! Ein Crash ist vorprogrammiert!
Hier müssen "Synchronistations" Techniken angewandt werden um die Bildung solcher Fehler vorzubeugen, die nur sehr schwer durch debugging entdeckt werden können!
Das ist ein sehr großes Thema, das ich demnächst auch in einer Serie weiter vorstellen werde und auch einige Beispiele dazu anfertigen!
Bisdahin kann ich euch den MSDN-Webcast zu diesem Thema empfehlen, der vom Einstieg über die Probleme auch einige Beispiele bietet! Zu finden hier!
Probleme beim Wechsel von VB6.0 auf .NET
Nun ist es schon knapp 12 Jahre her seit Microsoft die sechste Version von Visual Basic herausgebracht hat. Dann Später 2001 wurde die Sprache von Grund auf neu überarbeitet und komplett Objektorientiert gemacht. Gerade deswegen ist VB seit dem eine ernstzunehmende Sprache geworden.
Doch leider treffe ich vermehrt Leute in Foren oder Newsgroups, die ihre Projekte immer noch in VB6 gestalten. Obwohl diese Sprache doch schon sehr veraltet ist.
Doch was bringt einen Entwickler dazu eine alte Sprache zu verwenden, die auch noch mit einer sehr alten und unbequemen IDE daherkommt?
Dafür gibt es leider sehr viele (ja, auch gute) Gründe! Teilweise haben die "Alten Hasen" nicht die Lust sich in eine neue Sprache einzuarbeiten, denn dann müsste man ein komplett neues Programm Modell erlernen. (Objektorientierung)
Aber das ist einer der selteneren Gründe. Die häufigsten Gründe warum viele Firmen noch auf VB6 setzen sind folgende:
- Programmierer müssen umgeschult / weitergebildet werden.
- Es existiert bereits ein (großes) Projekt aus VB6 Zeiten. Wenn man dieses Projekt nun aktualisieren würde, würde das sehr viel Zeit und Geld kosten, desweiteren könnte Schaden durch Fehlprogrammierung entstehen!
- Viele Office / VBA Programmierer haben sich an die gleiche Syntax gewöhnt und wollen diese Bequemlichkeit nicht aufgeben!
- Unternehmen fürchten, dass ihre teuer programmierten Programme der Softwarepiraterie zum Opfer fallen könnten, da .NET binarys nicht nativ kompiliert werden! (Darüber mehr in einem Früheren Blogeintrag)

Ich selbst war mal von dem zweiten Punkt betroffen. Ich sollte eine VB6 Anwendung um neue Funktionen erweitern. Das war schon ein Krampf! Man merkt sehr schnell, um wieviel besser die neue IDE von Visual Studio und auch die Sprache an sich ist!
Leider war das Projekt zu wichtig und zu umfangreich und ich hätte sicher mehrere Monate gebraucht, um es auf .NET zu portieren.
Dabei stehen auf der anderen Seite sehr viele Vorteile:
- Zeitgemäße IDE
- Objektorientierung
- Zukunftsicherheit
- Zu anderen Sprachen kompatibler Code
- Leicht portierbare Projekte innerhalb .NET
- Managed Code
- (in Zukunft) Plattformunabhängigkeit
Da gibts natürlich noch viel mehr. Ich kann nur jedem stark empfehlen, auf .NET umzusteigen. Besonders da nun WPF stark im kommen ist und VB6 dieses neue System nicht unterstützen wird!
TreeView in XML speichern und zurück einlesen #2
Vor ca 2 Wochen habe ich eine Hilfsklasse zum Exportieren und Importieren von TreeNodes und XMLDateien veröffentlicht, mit dem Versprechen auf ein Update, mit dem das Exportieren ganzer TreeViews möglich ist.
Jetzt hatte ich Zeit und habe die Klasse um die entsprechenden Funktionen erweitert, sodass es nun möglich ist einen ganzen Baum in einer XML Datei abzulegen.
Warum habe ich das nicht gleich so gemacht? Ganz einfach weil ich das nicht brauchte!
Und es eigentlich sinnvoller finde die TreeNodes einzeln zu speichern!
Außerdem besteht beim Speichern einer TreeView ein großes Problem! Ansich eignet sich die Struktur einer TreeView perfekt um diese in einer XML Datei abzubilden. Nun kommt aber das Problem ins Spiel, dass eine TreeView mehrere "Kern" (root)-Äßte haben kann. Einer XML erlaubt das aber nicht. Eine XML Datei darf exakt eine Root-Node besitzen!
Wie umgeht man nun dieses Problem? Ich habe das so geregelt, dass ich einfach eine eigene Root-Node erstelle in die ich dann die Root-Nodes aus der TreeView einfüge die exportiert werden soll. Diese habe ich dementsprechend "rootNode" genannt! Also nicht erschrecken, wenn ihr in euren XML Files diese Fremde Node findet, diese wird beim Einlesen natürlich nicht mit eingelesen!
Ansonsten ähneld der neue Code sehr dem alten. Wie immer könnt ihr mir gerne Kommentare mit Bugs, Fehlern und Wünschen schreiben. Viel Spaß beim Testen und benutzen! Ich habe die Klasse wie auch letztes mal in ein Demoprojekt verpackt damit man die neuen Funktionen gleich live erleben kann! Wenn ihr nur auf die Klasse scharf seit, dann kopiert sie einfach aus dem Projektordner in euer eigenes Projekt!
Bei Weiteren Fragen schreibt mir bitte eine Mail!
Download: XMLParser v. 1.1
Java: Snake nachprogrammieren Demo
Letzte Woche habe ich versucht das iPhone Spiel Foold-it! in Java nach zu programmieren und heute habe ich eine weitere kleine Demojavaanwendung fertiggestellt, die sich an dem altbekannten Spiel Snake orientiert.
Irgendwie bin ich auf dem Geschmack gekommen Retro Games nach zu programmieren, das macht echt Spaß. heute will ich euch meine kleine Version von Snake "Sneaky" zeigen. Wie auch Fill IT ist dies ein reines Demospiel um das Prinzip deutlich zu machen. Deswegen habe ich auch hier nur die Basisfunktionalität eingebaut.
Als Vorbild habe ich mir hier das Snake genommen, das man von den ganz alten Nokia Handys kennt. Geniales und einfaches Spielprinzip mit Suchtfaktor!
Meine Umsetzung unterscheidet sich deswegen kaum. Es ist lediglich etwas bunter geworden, der Kopf der Schlange ist Rot und der Punkt der eingesammelt werden soll ist grün! Desweiteren habe ich eine Punkte Anzeige implementiert und auch einen Zähler die die Spielzeit in Sekunden anzeigt!
Wie auch in dem letzten Projekt könnt ihr euch sowohl die fertig kompilierte Binary laden oder gleich den Quelltext!
Mit dem Kommentieren des Quelltextes habe ich mir diesemal mehr Mühe gegeben, und viel mehr geschrieben, sodass ihr euch ganz gut zurecht finden werdet!
An dieser Stelle sei auch hier gesagt, dass ich noch ein Java Anfänger bin und der Code deswegen sehr wahrscheinlich nicht perfekt seit wird aber zum Üben dennoch sehr geeignet!
Wenn ihr Bugs oder Fehler findet, könnt ihr diese Gerne als Kommentar oder per Mail an mich senden, gern auch mit Lösung (ihr habt ja den Quelltext!
) Aber auch Verbesserungsvorschläge, was den Code angeht sind gern gesehen!
Ihr könnt es natürlich auch gern weiter entwickeln, schickt mir dann einen Screenshot oder sowas
Um den Quelltext verstehen zu können reichen auch Grundkenntnisse in Java und in diesen Themengebieten:
- Objektorientierte Programmierung (OOP)
- Grafische Programmierung (GUI)
- Vererbung
- Eventbehandlung



