Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Kompendium der Informationstechnik
 von Sascha Kersken
EDV-Grundlagen, Programmierung, Mediengestaltung
Buch: Kompendium der Informationstechnik
gp Kapitel 6 Konzepte der Programmierung
  gp 6.1 Algorithmen und Datenstrukturen
    gp 6.1.1 Ein einfaches Praxisbeispiel
    gp 6.1.2 Sortier-Algorithmen
    gp 6.1.3 Such-Algorithmen
    gp 6.1.4 Ausgewählte Datenstrukturen
  gp 6.2 Reguläre Ausdrücke
    gp 6.2.1 Muster für reguläre Ausdrücke
    gp 6.2.2 Programmierung mit regulären Ausdrücken
  gp 6.3 Systemnahe Programmierung
    gp 6.3.1 Prozesse und Pipes
    gp 6.3.2 Threads
  gp 6.4 GUI- und Grafikprogrammierung
    gp 6.4.1 Zeichnungen und Grafiken erstellen
    gp 6.4.2 Animation
    gp 6.4.3 Programmierung fensterbasierter Anwendungen
    gp 6.4.4 Java-Applets
  gp 6.5 Zusammenfassung

gp

Prüfungsfragen zu diesem Kapitel (extern)


Galileo Computing

6.3 Systemnahe Programmierung  downtop

Einige Programmieraufgaben erfordern Zugriffe auf Funktionen, die das Betriebssystem bereitstellt. In UNIX-Systemen werden solche Anweisungen als Systemaufrufe (system calls) bezeichnet. In diesem Abschnitt werden einige der wichtigsten Aspekte der systemnahen Programmierung behandelt: der Zugriff auf Dateien und Verzeichnisse, die Kommunikation zwischen verschiedenen Programmen und die gleichzeitige Ausführung mehrerer Aufgaben durch Prozesse und Threads.

Beinahe jedes größere Computerprogramm liest seine Daten oder Arbeitsanweisungen aus Dateien von Datenträgern und schreibt die Ergebnisse wieder dorthin. Die wichtigsten Funktionen für den Dateizugriff in C und Perl wurden bereits im vorigen Kapitel, Grundlagen der Programmierung, besprochen.

Dateizugriffe in Java

In Java stehen dagegen verschiedene Klassen für die unterschiedlichsten Aspekte der Datei-Ein- und -Ausgabe zur Verfügung Hier sehen Sie nur ein kurzes Beispiel: Das Programm in Listing 6.10 liest sämtliche Zeilen aus der als Kommandozeilenargument angegebenen Textdatei und gibt sie in umgekehrter Reihenfolge aus.

Listing 6.10   Zeilen aus einer Datei lesen und umgekehrt ausgeben (Java)

import java.io.*;
import java.util.*;
 
public class FileTurner
{
   public static void main (String args[])
   {
      try {
         turnFile (args[0]);
      }
      catch (FileNotFoundException e) {
         System.out.println ("Datei nicht gefunden!");
      }
      catch (IOException e) {
         System.out.println ("Dateifehler!");
      }
   }
      static void turnFile (String filename) throws 
FileNotFoundException, IOException
   {
      BufferedReader reader = new BufferedReader 
(new FileReader (filename));
      String zeile = "";
      Stack zeilen = new Stack();
      while ((zeile = reader.readLine()) != null) {
         zeilen.push (zeile);
      }
      while (!zeilen.empty()) {
         zeile = (String)zeilen.pop();
         System.out.println (zeile);
      }
   }
}

Das Programm demonstriert allerdings nicht nur, wie Sie in Java aus einer Textdatei lesen können, sondern zeigt noch einige andere interessante Aspekte der Java-Programmierung:

gp  Innerhalb der Methode turnFile() werden verschiedene Methoden aufgerufen, die Ausnahmen auslösen können. Damit nicht jede dieser Anweisungen innerhalb eines try/catch-Blocks stehen muss, werden sämtliche möglichen Ausnahmen mit Hilfe einer throws-Klausel an die aufrufende Stelle delegiert. Aus diesem Grund steht der Aufruf von turnFile() innerhalb von main() innerhalb eines try-Blocks; die anschließenden catch-Blöcke fangen die entsprechenden Ausnahmen ab: java.io.FileNotFoundException (Datei nicht gefunden) und java.io.IOException (Schreib-/Lesefehler).
gp  Für das Umkehren der Zeilen aus der Textdatei wird die spezielle Datenstruktur java.util.Stack verwendet, mit deren Hilfe in Java die in Abschnitt 6.1 besprochene Stack-Funktionalität implementiert wird: Die Methode push() fügt ein beliebiges Objekt (eine Instanz der allgemeinsten Klasse java.lang.Object) am Ende des Stacks hinzu, pop() entnimmt das letzte Objekt und gibt es zurück. Mit Hilfe von Typecasting kann es in ein Objekt seiner ursprünglichen Klasse zurückverwandelt werden: im vorliegenden Fall (String)zeilen.pop().
gp  Zum Lesen ganzer Zeilen aus der Textdatei wird der java.io.FileReader, der dem Lesen aus einer Datei dient, von einem java.io.BufferedReader umhüllt, dessen Methode readLine() jeweils eine Zeile aus dem zugrunde liegenden Eingabestrom liest. Die Bedingung der while()-Schleife macht es sich übrigens zunutze, dass readLine() am Dateiende null zurückgibt: Die Wertzuweisung zeile = reader.readLine() wird dazu unmittelbar mit null verglichen.

Galileo Computing

6.3.1 Prozesse und Pipes  downtop

Das Prinzip des Prozesses wurde bereits in Kapitel 4, Betriebssysteme, angesprochen: Jedes unter einem modernen Betriebssystem laufende Programm wird als separater Prozess oder Task ausgeführt, der seinen eigenen Speicherbereich und seine eigenen Ein- und Ausgabeschnittstellen besitzt. Threads dagegen stellen eine einfache Möglichkeit zur Verfügung, innerhalb desselben Prozesses mehrere Aufgaben parallel zu erledigen. In diesem Unterabschnitt werden beide Verfahren kurz praktisch vorgestellt.

Das Erzeugen neuer Prozesse ist das herkömmliche Verfahren, um in UNIX-Programmen mehrere parallele Verarbeitungseinheiten zu realisieren: Zwei verschiedene Aufgaben müssen nicht aufeinander warten, um ausgeführt zu werden. Dies ermöglicht beispielsweise die effektive Nutzung von Wartezeiten, die durch die verhältnismäßig langsamen I/O-Operationen entstehen können.

fork( )

Das UNIX-Prozessmodell erzeugt einen neuen Prozess durch den Systemaufruf fork(), der eine absolut identische Kopie des ursprünglichen Prozesses erzeugt. Jede Codezeile, die hinter einem Aufruf von fork() steht, wird in nicht vorhersagbarer Reihenfolge doppelt ausgeführt. Da der ursprüngliche Prozess (Parent-Prozess) in der Regel etwas anderes tun soll als der neu erzeugte (Child-Prozess), müssen diese beiden irgendwie voneinander unterschieden werden. In diesem Zusammenhang ist es nützlich, dass fork() im Parent-Prozess die Prozess-ID (PID) des Child-Prozesses zurückgibt und im Child-Prozess 0. Folglich sehen praktisch alle fork()-Aufrufe in UNIX-C-Programmen schematisch so aus:

int f;
...
if (f = fork()) {
   ...
   /* Parent-Prozess:
      Hier werden die Parent-Aufgaben erledigt;
      f enthält PID des Childs.               */
} else {
   ...    /* Child-Prozess:
      Hier werden die Child-Aufgaben erledigt;
      getppid() liefert PID des Parents.      */
}

Um die Forking-Funktionalität in C-Programmen verwenden zu können, müssen Sie die Header-Datei sys/types.h einbinden:

#include <sys/types.h>

Unter Windows wird dieses Verfahren von Hause aus nicht unterstützt; die Win32-API definiert stattdessen eine Funktion namens CreateProcess(), die einen neuen, leeren Prozess erzeugt. In neuen Windows-Versionen von Perl wird trotzdem eine UNIX-kompatible fork()-Funktion angeboten. Aufruf und Funktionalität von fork() sind in Perl übrigens mit C identisch.

Das folgende Beispiel zeigt ein kleines Perl-Programm, das im Parent- und im Child-Prozess jeweils eine Schleife von 1 bis 10.000 durchzählt und anzeigt. Im Parent-Prozess werden die Werte ein wenig eingerückt:

#!/usr/bin/perl
if (fork()) {
   # Parent-Prozess
   for ($i = 1; $i <= 10000; $i++) {
      print "     $i\n";
   }
} else {
   # Child-Prozess
   for ($i = 1; $i <= 10000; $i++) {
      print "$i\n";
   }
}

Wenn Sie das Programm ausführen, werden Sie den Wechsel zwischen ein- und ausgerückten Zahlen bemerken; Sie können die Ausgabe auch genau analysieren, indem Sie sie mittels >Dateiname in eine Datei umleiten.

Natürlich ist dieses Beispiel nicht besonders sinnvoll. Zu den bedeutendsten Aufgaben von fork() gehört die Implementierung von Netzwerkservern, die mit mehreren Clients zur gleichen Zeit kommunizieren. Ein Beispiel finden Sie in Kapitel 14, Netzwerkanwendungen.

Neben fork() gibt es noch einige weitere Anweisungen, die im Zusammenhang mit Prozessen nützlich sind:

gp  exec(), verfügbar in C und Perl, führt das Programm aus, dessen Pfad als Argument angegeben wurde, und kehrt nicht zurück – nachdem das Programm beendet ist, wird auch der Prozess beendet. Dies ist nützlich, um einen mittels fork() erzeugten Prozess mit der Ausführung eines externen Programms zu beauftragen.
gp  system(), ebenfalls in C und Perl einsetzbar, führt das als Argument angegebene Programm aus und kehrt anschließend zurück. Dies wird vorzugsweise für den Aufruf von Systembefehlen genutzt. Beispielsweise können Sie sich in einem UNIX-Programm folgendermaßen den Inhalt des aktuellen Verzeichnisses anzeigen lassen:
system ("ls");
    Unter Windows muss die Anweisung natürlich modifiziert werden, weil der Befehl für die Verzeichnisanzeige hier anders lautet:
       
system ("dir");
gp  Der Backtick-Operator (der Pfad eines Programms in ``) ist nur in Perl (und in diversen UNIX-Shells) verfügbar und liefert die Ausgabe des aufgerufenen externen Programms als Wert zurück. Es ist ein wenig umständlich, das Zeichen ` zu erzeugen: Da es sich um ein Akzentzeichen handelt, müssen Sie zunächst (Shift) + (´) drücken und anschließend die (Leertaste). Backticks sind extrem nützlich, um die Ausgabe eines Befehls genau zu analysieren – besonders im Zusammenhang mit Perls RegExp-Fähigkeiten.
    Der folgende Code gibt auf einem UNIX-System beispielsweise sämtliche Dateien des ausführlichen Inhaltsverzeichnisses zurück, in denen der Dateiname (letzter Posten in der Zeile) den Buchstaben a enthält:
       
$dir = `ls -l`;
@dirs = split (/\n/, $dir);
foreach $d(@dirs) {
   print "$d\n" if $d =~ /a[^\s]+$/;
}

Kommunikation zwischen Prozessen durch eine Pipe

Mit Hilfe von fork() können Sie zwar beliebig viele Prozesse erzeugen, aber diese Prozesse laufen völlig beziehungslos nebeneinander. In der bisher vorgestellten Form sind sie also nicht dafür geeignet, mehrgliedrige Aufgaben kooperativ zu erledigen. Da Programme und Tasks in der Praxis jedoch auf vielfältige Weise miteinander interagieren müssen, sind Mittel zur Kommunikation zwischen den verschiedenen Prozessen erforderlich. In Kapitel 4, Betriebssysteme, wurde bereits angedeutet, dass es vielerlei Möglichkeiten dafür gibt, unter anderem Shared Memory, Signale oder Pipes. Als wichtigstes Beispiel wird hier die Verwendung von Pipes angesprochen.

Pipes in Perl-Programmen

Die Verwendung von Pipes zur Kommunikation wird hier am Beispiel von Perl demonstriert, da sie hier besonders leicht und effizient vonstatten geht. Im Grunde gibt es keinen Unterschied zwischen der Anwendung einer Pipe auf der Kommandozeile und aus einem Perl-Programm heraus. In einem Perl-Programm erzeugen Sie eine Pipe zu einem anderen Programm, indem Sie mittels open ein Dateihandle öffnen und statt des üblichen Dateinamens ein lauffähiges Programm angeben. Ein Pipe-Symbol vor dem Programmnamen ("|Programm") bedeutet dabei, dass Sie die Ausgabe Ihres Programms an dieses Programm weitergeben möchten, während ein dahinter stehendes Pipe-Symbol es Ihnen ermöglicht, zeilenweise aus der Ausgabe des externen Programms zu lesen.

Beispielsweise können Sie die Ausgabe Ihres Programms folgendermaßen an den Linux-Pager less weiterleiten (unter Windows könnten Sie stattdessen "|more" schreiben), um Daten bildschirmweise auszugeben:

open (AUSG, "|less");
# Ausgabebefehl:
print AUSG "Text...";

Auch die Eingabe aus einer Pipe ist unproblematisch. Sie können zum Beispiel folgendermaßen zeilenweise aus der UNIX-Prozesstabelle lesen:

open (EING, "ps aux|");
while ($prozess = <EING>) {
   # ... Zeile verarbeiten
}

Der Systemaufruf pipe( )

Eine bequemere Möglichkeit, eine Pipe für die Kommunikation zwischen zwei Prozessen innerhalb Ihres Programms einzusetzen, bietet der Systemaufruf pipe(). Die gleichnamige Perl-Anweisung erzeugt zwei durch eine Pipe verbundene Dateihandles, deren Namen Sie durch Übergabe entsprechender Argumente festlegen können. Das erste Argument gibt das lesende, das zweite das schreibende Dateihandle an:

pipe (READER, WRITER);

In dem kleinen Programm in Listing 6.11 erzeugt der Parent-Prozess in einer for-Schleife eine Reihe von Zahlen, die in das Schreibhandle geschrieben werden. Der Child-Prozess liest sie aus dem zugehörigen Lesehandle und berechnet jeweils ihr Quadrat. Besonders sinnvoll ist diese Anwendung nicht, demonstriert aber das Prinzip.

Listing 6.11   Eine kleine pipe( )-Demonstration

#/usr/bin/perl -w
use strict;
pipe (READER, WRITER);
my $child = fork();
if ($child) {
   # Parent-Prozess liest nur; WRITER schließen
   close WRITER;
   while (my $line = <READER>) {
      chomp $line;
      print ("Das Quadrat von $line ist ".            $line * $line. "\n");
   }
} else {
   # Child-Prozess schreibt nur; READER schließen
   close READER;
   for (my $i = 0; $i < 100; $i++) {
      print "Bin bei $i\n";
      print WRITER "$i\n";
   }
}

Wie Sie bemerken, schließt jeder der beiden Prozesse zu Beginn eines der beiden Pipe-Handles. Es würde auch nichts nützen, das nicht zu tun, da das Pipe-Handle nur in eine Richtung funktioniert, für die Sie sich von vornherein entscheiden müssen. Im vorliegenden Fall behält der Child-Prozess das schreibende Dateihandle offen, der Parent-Prozess das lesende. Wenn die Kommunikation in zwei Richtungen stattfinden soll, müssen Sie über einen zweiten pipe()-Aufruf ein weiteres Paar von Dateihandles erzeugen.


Galileo Computing

6.3.2 Threads  toptop

Eine ähnliche Technik wie das Erzeugen mehrerer Prozesse ist das Erzeugen mehrerer Threads innerhalb eines Prozesses. Genau wie das Betriebssystem dafür sorgt, dass die verschiedenen Prozesse abwechselnd abgearbeitet werden, findet auch die Verarbeitung der Threads im Wechsel statt; in den meisten Fällen kann wie bei Prozessen eine Priorität gewählt werden. Der Hauptunterschied zwischen Prozessen und Threads besteht darin, dass Threads im selben Speicherraum laufen und gemeinsamen Zugriff auf Ressourcen besitzen.

Inzwischen unterstützen alle modernen Betriebssysteme Threads, unter Umständen wird diese Unterstützung auch von der Bibliothek der verwendeten Programmiersprache statt vom System selbst zur Verfügung gestellt. Allerdings ist das Programmieren von Multithreading-Anwendungen nicht in allen Sprachen einfach, weil es sich um ein relativ neues Verfahren handelt, das noch nicht in allen Sprachen standardisiert ist.

Threads in Java

Am praktischsten lässt sich die Programmierung von Threads am Beispiel von Java erläutern, weil die Thread-Funktionalität in dieser Sprache von Anfang an verankert wurde. Sie wird im Wesentlichen durch die Klasse java.lang.Thread bereitgestellt, die einen einzelnen lauffähigen Thread repräsentiert.

Wenn eine Java-Instanz als Thread ausgeführt werden soll, muss die zugrunde liegende Klasse eine der beiden folgenden Bedingungen erfüllen:

gp  Sie muss von java.lang.Thread abgeleitet sein. Innerhalb dieser Klasse können Sie die Methode run() überschreiben, die die eigentlichen Anweisungen enthält, die als Thread ausgeführt werden sollen.
gp  Alternativ kann sie das Interface Runnable implementieren. Zu diesem Zweck muss sie ebenfalls eine Methode namens run() bereitstellen.

Wenn Sie eine Klasse von Thread ableiten, können Sie eine Instanz davon erzeugen; ein Aufruf ihrer Methode start() beginnt mit der Ausführung der Anweisungen in run(). Eine Instanz einer Klasse, die Runnable implementiert, wird dagegen als Argument an den Konstruktor der Klasse Thread übergeben. Auch dieser neue Thread wird mittels start() gestartet. Letzteres ist besonders nützlich, wenn Sie innerhalb des aktuellen Programms einen zweiten Thread starten möchten, ohne eine externe Instanz zu verwenden: Sie können Runnable einfach durch Ihr aktuelles Programm implementieren.

In beiden Fällen läuft das aktuelle Hauptprogramm ebenfalls als Thread weiter. Dass jedes Java-Programm automatisch ein Thread ist, können Sie besonders gut an Fehlermeldungen bemerken. Diese lauten häufig, es sei ein Fehler im Thread main aufgetreten.

Das Beispiel in Listing 6.12 zeigt eine von Thread abgeleitete Klasse namens BGSearcher, die im Hintergrund ein als Argument übergebenes Array von int-Werten linear nach einem ebenfalls übergebenen Wert durchsucht. Die Klasse BGSearchTest ist ein Testprogramm für BGSearcher, das ein Array mit Zufallswerten füllt und an den Hintergrundsucher übergibt. Schließlich benötigen Sie noch das Interface SearchInfo, das von einer Klasse, die den BGSearcher verwenden möchte, implementiert werden muss. Tippen Sie alle drei Klassen ab und speichern Sie sie in entsprechenden .java-Dateien im gleichen Verzeichnis. Es genügt anschließend, die Datei BGSearchTest.java zu kompilieren – die beiden anderen werden automatisch mitkompiliert.

Listing 6.12   Lineare Suche im Hintergrund als Java-Klasse

public class BGSearcher extends Thread
{
   private int[] liste;
   private int wert;
   private SearchInfo info;
      public BGSearcher (int[] l, int w, SearchInfo n)    {
      this.liste = l;
      this.wert = w;
      this.info = n;
   }
      public void run ()
   {
      for (int i = 0; i < liste.length; i++) {
         if (liste[i] == wert)             info.searchinfo (i);
      }
   }
}
 
 
public class BGSearchTest implements SearchInfo
{
   public static void main (String args[])
   {
      int werte[] = new int[10000];
      System.out.println ("Erzeuge Zufallswerte!");
      for (int i = 0; i < 10000; i++) {
         werte[i] = (int)(Math.random() * 10) + 1;
      }
      // Programm-Instanz zur Übergabe an BGSearcher:
      BGSearchTest test = new BGSearchTest();
      // Sucher erzeugen und aufrufen
      BGSearcher searcher =            new BGSearcher (werte, 4, test);
      searcher.start();
      // Eigenen Aufgaben nachgehen
      for (int i = 0; i < 1000; i++) {
         System.out.println ("Bin jetzt bei " + i);
      }
   }
      public void searchinfo (int pos)
   {
      System.err.println ("Gefunden bei " + pos);
   }
}
 
 
public interface SearchInfo
{
   public void searchinfo (int pos);
}

Auch für dieses Beispiel sind wieder einige Erklärungen fällig:

gp  Die Klasse BGSearcher ist von Thread abgeleitet, damit die Methode run() in einem Thread laufen kann. Die Methode start(), die ein Programm aufrufen muss, damit eine Instanz von BGSearcher mit der Arbeit beginnt, muss hier nicht explizit definiert werden, sie wird von Thread geerbt.
gp  Der Konstruktor von BGSearcher erwartet drei Argumente: das zu durchsuchende int-Array, den gesuchten Wert und eine Instanz von SearchInfo. Letzteres kann eine Instanz einer beliebigen Klasse sein, die das Interface SearchInfo implementiert – in der Regel, wie im vorliegenden Fall, das Programm, das die Hintergrundsuche einsetzt.
    Die Übergabe der SearchInfo-Instanz ist notwendig, damit BGSearcher jedes Mal eine Methode des implementierenden Programms aufrufen kann, wenn der gesuchte Wert im Array gefunden wird. Eine solche Methode, die Sie in einem eigenen Programm bereitstellen, damit sie bei Bedarf von außen aufgerufen wird, heißt Callback-Methode. Der Einsatz von Callbacks ist immer dann sinnvoll, wenn Sie ein externes, asynchron auftretendes Ereignis (hier zum Beispiel einen Sucherfolg) in Ihrem eigenen Code verarbeiten möchten.
       
gp  Die Klassendefinition des Testprogramms BGSearchTest enthält die Klausel implements SearchInfo. Mit dieser Klausel garantiert die Klasse, dass sie Implementierungen der Methoden des genannten Interfaces (in diesem Fall nur die eine Methode searchinfo()) bereitstellt. Dies gibt anderen Klassen die Sicherheit, ein Objekt der Klasse, die das Interface implementiert, als Instanz dieses Interfaces zu betrachten. Gerade die hier gezeigte Verwendung einer Callback-Methode ist ein hervorragendes Beispiel für die Nützlichkeit eines Interfaces: Ein Programm kann BGSearcher einsetzen, wenn es SearchInfo implementiert, es kann ansonsten von einer beliebigen Klassenhierarchie abstammen.
gp  In der Methode main() erzeugt BGSearchTest ein 10.000 Elemente großes Array, das mit zufälligen int-Werten gefüllt wird. Für die Erzeugung von Zufallszahlen zwischen 1 und 10 wird die Zufallsgenerator-Methode Math.random() verwendet, die pseudozufällige Fließkommawerte zwischen 0 und 1 zurückgibt. Mit Hilfe von Multiplikation, Addition und Typecasting wird der erhaltene Wert in den richtigen Bereich umgerechnet.
gp  Die Anweisung in BGSearchTest, die vielleicht am merkwürdigsten erscheint, ist die Erzeugung einer Instanz der Klasse selbst:
BGSearchTest test = new BGSearchTest();
    Die erzeugte Instanz test ist erforderlich, um sie als Referenz auf das aktuelle Programm an das als Nächstes erzeugte BGSearcher-Objekt zu übergeben, damit es wiederum die Callback-Methode searchinfo() aufrufen kann.
       
gp  Nachdem das BGSearcher-Objekt erzeugt ist, muss übrigens seine Methode start() aufgerufen werden, damit es mit der eigentlichen Suche beginnt. Anschließend kann sich das Programm um seine eigenen Aufgaben kümmern, weil der Wechsel zwischen den beiden Threads automatisch vonstatten geht.
gp  Die Callback-Methode searchinfo() wird automatisch jedes Mal aufgerufen, wenn der gesuchte Wert im Array gefunden wird. Sie erhält als Übergabewert die Suchposition. Im vorliegenden Beispielprogramm tut die Methode nichts besonders Interessantes mit dem Wert, sie gibt ihn nur aus.
    Bemerkenswert ist lediglich, dass die Ausgabe auf den Ausgabestrom System.err erfolgt, die Standardfehlerausgabe (in UNIX und C stderr genannt). Es handelt sich um einen alternativen Konsolen-Ausgabekanal speziell zum Anzeigen von Warnungen und Fehlermeldungen. Besonders nützlich ist die Standardfehlerausgabe, wenn Sie die eigentliche Ausgabe mittels >Dateiname in eine Datei umgeleitet haben: Die Ausgabe auf stderr garantiert, dass wichtige Meldungen nicht mit der normalen Ausgabe eines Programms in der Umleitung landen, sondern auf dem Bildschirm angezeigt werden.
       
gp  Das Interface SearchInfo deklariert, wie bei Interfaces üblich, lediglich den leeren Header der Methode searchinfo(), die von einer Klasse bereitgestellt werden muss, welche SearchInfo implementieren möchte.

Im nächsten Abschnitt finden Sie ein weiteres Beispiel für die Anwendung von Threads: die flimmerfreie Animation.

  

Einstieg in PHP 5

Einstieg in Java

C von A bis Z

Einstieg in C++

Einstieg in Linux

Einstieg in XML

Apache 2




Copyright © Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de