Concurrency

  • C++0x

    Surprisingly, C++0x feels like a new language: The pieces just fit together better 
    than they used to and I find a higher-level style of programming more natural than before
    and as efficient as ever. (Bjarne Stroustrup)
    right C++0x bricht nicht mit C++ Code.

    Historie

    • Zeitachse C++:
      C__Timeline.gif
    • ARM C++
      • Annotated C++ Reference Manual (ARM)
      • erster C++ Standard
      • Grundlage für C++98
      • Bjarne Stroustrup definiert C++ Funktionsumfang
    • C++98 (ISO/IEC 14882)
      • aktuelle ISO C++ Standard
    • C++03
      • technische Korrektur des Standards
    • TR1
      • Technical Report 1
      • Ergänzungen der C++ Standard Library

    Ziele von C++0x

    Den

    Prinzipien von C++

    • C++ ist eine Multi-Paradigmen Programmiersprache.
    • Vertraut dem Programmierer,
    • Zahle nicht für das, was du nicht benutzt.
    • Brich keinen funktionierenden Code.
    • Kontrolle zur Kompilezeit ist besser als zu Laufzeit.
    bleibt C++0x treu und legt insbesondere Wert auf

    Prinzipien von C++0x

    • Bessere Systemprogrammierung ermöglichen.
    • Bessere Sprache für die Implementierung von Bibliotheken.
    • Soll leichter zu lehren und zu lernen sein.

    Kernsprache

    Mächtigere Klassendefinition

    Ein paar neue Konstrukte:
    class NonCopyableClass{
    NonCopyableClass & operator=(const NonCopyableClass&) = delete;

    NonCopyableClass(const NonCopyableClass&) = delete;
    NonCopyableClass() = default;

    };

    class BaseClass{
    virtual void foo();

    void bar();
    };

    class DerivedClass [[base_check]]: public BaseClass{

    void foo [[override]] ();
    void foo [[override]](int)

    void bar [[hiding]] ();
    void bar [[hiding]](int);

    };

    class DelegateConstructor {
    int value;
    public:

    DelegateConstructor(int v) : value(v) {}

    DelegateConstructor() : DelegateConstructor(17) { }
    };


    class BaseClass{
    public:
    BaseClass(int i);

    };

    class DerivedClass : public BaseClass{
    public:

    using BaseClass::BaseClass;
    };

    template<typename T> class MyVector {

    public:
    MyVector(std::initializer_list<T> list); // initializer-list constructor
    MyVector(const MyVector&); // copy constructor

    MyVector(MyVector&&); // move constructor
    MyVector& operator=(const MyVector&); // copy assignment

    MyVector& operator=(MyVector&&); // move assignment
    };

    defaulted und deleted Standardmethoden

    • die Klasse NonCopyableClass ist nicht kopierbar
    • delete: unterdrückt sowohl den automatisch erzeugten Kopier- als auch den Zuweisungsoperator
    • default: ein Defaultkonstruktor wird erzeugt, für dessen Implementierung der Kompiler sorgt
      • implementiert der Anwender in einer Klasse ein Konstruktor, wird in C++ der Defaultkonstruktor nicht erzeugt right der Anwendert muß diesen selber schreiben
    • void *operator new(std::size_t) = delete; führt in einer Klasse dazu, das diese nicht mehr dynamisch mittels new erzeugt werden kann

    override und hiding von Methoden

    • durch das Attribut base_check der Klasse DerivedClass muß das Überschreiben oder Verstecken von Basisklassenmethoden explizit ausgedrückt werden
    • override: diese Methode will eine Methode überschreiben
    • hidding: diese Methode will eine Methode verstecken (void bar() versteckt die gleichnamige Methode der Basisklasse)
    • sowohl void foo [[override]](int) als auch void bar [[hidding]](int) sind Syntaxfehler

    Delegation von Konstruktoren

    • gemeinsame Funktionalität aller Konstruktoren wurde in C++ in einer init() Methode angeboten, die alle Konstruktoren aufzurufen hatten
    • die Konstruktoren der Klasse DelegateConstructor rufen explizit oder implizit den Konstruktor DelegateConstructor(int v) auf, der darfür sorgt, das value auf 17 initialisiert wird

    Vererbung von Konstruktoren

    • using BaseClass::BaseClass: alle Konstruktoren der Basisklasse in der abgeleiteten Klasse stehen zur Verfügung

    Initialiser List Konstruktor

    • MyVector(std::initializer_list list) ist ein neuer Konstruktor, der über eine Sequenz von homogenen Wert initialisiert werden kann: MyVector myIntVec={1,2,3,4,5}
    • jeder Standardcontainer (vector, list, deque, map, multimap, set, multiset) besitzen diesen Konstruktor, so dass sich diese direkt mit Aggregaten befüllen lassen
    • eigene Datentypen oder Funktionen können auch direkt mittels der Sequenz (std::initializer_list list) mit Aggregaten umgehen:
      double sumAll(std::initializer_list list)

    Move Semantic

    • Ziel der Move Semantic ist es unnötiges Kopieren zu vermeiden
    • MyVector(MyVector&&); und MyVector& operator=(MyVector&&) werden move constructor und move assignment genannt
    • syntaktisch unterscheiden sich diese vom copy constructor und copy assignment durch zwei && (rvalue Referenzen)
    • sie statten die Daten mit einer Move Semantic aus, den beim Erzeugen von neuen aus alten Objekten werden die Inhalte transferiert und nicht kopiert
    • Beispiel: Copy Semantic versus Move Semantic
    std::cout << "move semantic for vector: " << std::endl;
    std::vector <int> vec1={1,2,3,4,5,6,7,8,9};

    std::vector <int> vec2;
    std::cout << "vec1.size(): " << vec1.size() << " vec2.size(): " << vec2.size() << std::endl;

    vec2= std::move(vec1);
    std::cout << "vec1.size(): " << vec1.size() << " vec2.size(): " << vec2.size() << "\n\n";

    std::cout <<"copy semantiv for vector: " << std::endl;
    std::vector <int> vec3;

    std::cout << "vec2.size(): " << vec2.size() << " vec3.size(): " << vec3.size() << std::endl;

    vec3= vec2;
    std::cout << "vec2.size(): " << vec2.size() << " vec3.size(): " << vec3.size() << std::endl;
    • der Codeschnipsel erzeugt folgende Ausgabe
      move semantic for vector:
      vec1.size(): 9 vec2.size(): 0
      vec1.size(): 0 vec2.size(): 9

      copy semantiv for vector:
      vec2.size(): 9 vec3.size(): 0
      vec2.size(): 9 vec3.size(): 9
    • das ganze visualisiert
      • Move Semantik:
        MoveSemantic.gif
      • Copy Semantik:
        CopySemantic.gif

    Die Move Semantic stellt bei Container ein nicht zu unterschätzendes Optimierungspotential dar. Sowohl die Initializer List als auch die Move Semantic Konstruktoren bietet jeder Standardcontainer in C++0x an.

    Einfacheres Arbeiten mit der Standard Library

    Der hochaktuelle gcc 4.5 kann bis auf die range basierte for loop for (auto itr : myInt) { std::cout <<  *itr << " ";} das folgende Codeschnipsel übersetzten

    std::vector <int> myInt(10);
    std::cout << "vector of 10 times 0: ";

    for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){

    std::cout << *itr << " ";
    }
    std::cout << "\n\n";

    // for (auto itr : myInt) { std::cout << *itr << " ";}
    // for (std::vector<int>::const_iterator itr= intInt.begin(); itr != myInt.end(); ++itr){... }

    std::iota(myInt.begin(), myInt.end(),1);

    std::cout << "Increment each value by 1: ";
    for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){

    std::cout << *itr << " ";
    }
    std::cout << "\n\n";

    std::sort(myInt.begin(),myInt.end(),[](int a, int b){ return a >= b;} );

    std::cout << "Sort the vector of natural numbers: ";
    for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){

    std::cout << *itr << " ";
    }
    std::cout << "\n\n";

    std::cout<< "std::min({1,2,4,0,1,-5,2}): " << std::min({1,2,4,0,1,-5,2}) << "\n\n";

    std::map<std::string,std::string> phonebook =
    { {"Bjarne","+1 (012) 555-1212"},

    {"Herb", "+1 (345) 555-3434"},
    {"Alexandrei","+1 (678) 555-5656"} };

    std::cout << "Get each telephon number: \n";
    for (auto itr = phonebook.begin(); itr!= phonebook.end(); ++itr){

    std::cout << itr->first << ": " << itr->second << "\n;

    }
    • und die Ausgabe
      vector of 10 times 0: 0 0 0 0 0 0 0 0 0 0

      Increment each value by 1: 1 2 3 4 5 6 7 8 9 10

      Sort the vector of natural numbers: 10 9 8 7 6 5 4 3 2 1

      std::min({1,2,4,0,1,-5,2}): -5

      Get each telephon number:
      Alexandrei: +1 (678) 555-5656
      Bjarne: +1 (012) 555-1212
      Herb: +1 (345) 555-3434

    Initialiser Listen

    • {}-Initialiser bieten einheitliche Initialisierung von Objekten an und können für alle Initialisierungen verwendet werden
    • Beispiele:
      • Funktionsaufruf: std::min({1,2,4,0,1,-5,2})
      • std::map: std::map<std::string,std::string> phonebook = { {"Bjarne","+1 (012) 555-1212"} }
      • std::vector: std::vector vec1={1,2,3,4,5,6,7,8,9}
    Base x = Base{1,2};  
    Base* p = new Base{1,2};


    struct Derived : Base {
    Derived(int x, int y) :Base{x,y} {};

    };

    func({a}); // function invocation
    return {a}; // function return value

    Neue Algorithmen

    • std::iota(myInt.begin(), myInt.end(),1) inkrementiert die Elemente des Vektors sukzessive um 1
    • std::min({1,2,4,0,1,-5,2},[](int a,int b){ return abs(a) >= abs(b)})kann eine Vergleichsfunktion übergeben werden
      • neben Funktionspointern und Funktionsobjekten (Funktor) kann die Vergleichsfunktion direkt definiert werden (Lambda Funktion)
    • die restlichen neuen Algorithmen: http://www2.research.att.com/~bs/C++0xFAQ.html#algorithms

    Lambda Funktionen

    • Aufbau: [Bindung](Argumente){Funktionskörper}
      • []: Bindung an den lokalen Scope (Lambda Funktionen sind Closures)
        • []: keine Bindung
        • [=]: Werte werden kopiert
        • [&]: Werte werden referenziert
      • (): Argumente (optional) der Lambda Funktion
      • {}: Funktionskörper,
        • ein einzelner Ausdruck ist gleichzeitig der Returnwert
        • kann auch Anweisungen enthalten right return Anweisung notwendig
    • die Details können unter http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=25 nachgelesen werden
    • Beispiele:
    std::map< const char , std::tr1::function<double(double,double)> > dispTable;

    dispTable.insert( std::make_pair('+',[](double a, double b){ return a + b;}));

    dispTable.insert( std::make_pair('-',[](double a, double b){ return a - b;}));

    dispTable.insert( std::make_pair('*',[](double a, double b){ return a * b;}));

    dispTable.insert( std::make_pair('/',[](double a, double b){ return a / b;}));

    std::cout << "3+4= " << dispTable['+'](3,4) << std::endl;

    std::cout << "3-4= " << dispTable['-'](3,4) << std::endl;

    std::cout << "3*4= " << dispTable['*'](3,4) << std::endl;

    std::cout << "3/4= " << dispTable['/'](3,4) << std::endl;

    []{
    std::cout << "inline lambda";
    std::cout << " function.\n";

    }();

    auto myAdd= [](int a, int b){return a + b;};

    std::cout << "myAdd(1,2): " << myAdd(1,2) << std::endl;
    • dispTable ist ein dispatch Table mit Hilfe von Lambda Funktionen und den neuen Wrapper für Funktionsobjekte std::tr1
    • die namenslose Funktion wird in-place augerufen
    • myAdd bindet die lambda Funktion, dessen Typ durch auto bestimmt wird
    • Ausgabe:
      3+4= 7
      3-4= -1
      3*4= 12
      3/4= 0.75
      inline lambda function.
      myAdd(1,2): 3

    Type Inference

    auto
    • auto erlaubt es, den Typ aus einem Initialiser abzuleiten
    • damit können Schleifen deutlich kompaker geschrieben werden
    std::vector <int> myInt{1,2,3,4,5};

    for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){

    std::cout << *itr << " ";
    }
    for (auto itr : myInt) {
    std::cout << *itr << " ";

    }
    decltype
    • decltype erlaubt es, den Typ aus einem Ausdruck abzuleiten
    • damit lässt sich der Typ von komplexen Berechnungen ohne Template Metaprogramming Magic bestimmen
    std::vector <int > myVecInt={1};
    std::vector <double> myDoubleInt={1.1};

    decltype(myVecInt[0]*myDoubleInt[0]) myDouble = 2.1;

    void foo(const vector<int>& a, vector<float>& b){

    typedef decltype(a[0]*b[0]) ErgTyp;

    for (int i=0; i<b.size(); ++i) {

    ErgTyp* p = new ErgTyp(a[i]*b[i]);

    ...
    • myDouble besitzt letztendlich den Typ, den es verspricht
    • typedef decltype(a[0]*b[0]) ErgTyp erklärt einen neuen Typ, der sich aus der Multiplikation ergibt right float

    Generische Programmierung

    Variadic Templates

    • Templates, die eine beliebige Anzahl von Elementen annehmen können
    • das folgende Programm stellt zwei Variadic Templates vor
      • die Templatefunktion f kann mit einer beliebigen Zahl von Argumenten umgehen
      • die Templatefunktion printCommaSeparatedList, die in zwei Ausprägungen vorliegt, gibt eine beliebige Anzahl von Elementen aus
    #include <iostream>
    #include <string>
    #include <vector>

    template <typename... Args>
    int f(Args... args){

    return (sizeof... args);
    }

    template<typename T>

    void printCommaSeparatedList(T value)
    {
    std::cout<<value<<std::endl;

    }

    template<typename First,typename ... Rest>
    void printCommaSeparatedList(First first,Rest ... rest)

    {
    std::cout<<first<<",";
    printCommaSeparatedList(rest...);
    }


    int main() {
    std::cout << "\n";

    auto intList= {1,2,3};
    std::vector <int> intVec= {1,2,3,4,5};

    std::cout << "f() has " << f() << " arguments\n";

    std::cout << "f(42, 3.14) has " << f(42, 3.14) << " arguments\n";

    std::cout << "f(\"one\",\"two\",\"three\",\"four\") has " << f("one","two","three","four" )
    << " arguments\n";

    std::cout << "f(intVec,intList) has " << f(intVec,intList) << " arguments\n\n";

    printCommaSeparatedList("Only primary template used.");
    printCommaSeparatedList(42,"hello",2.3,'a');

    std::cout << "\n";
    }
    • erzeugt die Ausgabe:
      f() has 0 arguments
      f(42, 3.14) has 2 arguments
      f("one","two","three","four") has 4 arguments
      f(intVec,intList) has 2 arguments

      Only primary template used.
      42,hello,2.3,a
    • Wie funktioniert nun das ganze?
      • das entscheidende sind die drei Punkte, die entweder links template <typename... Args> oder rechts int f(Args... args){ vom Parameter Argsstehen
        • links von Parameter packt der Ellipsen-Operator... das sogenannte parameter pack, rechts entpackt er es wieder
      • eine Besonderheit ist der neue Operator sizeof... , der direkt mit parameter packs umgehen kann
      • f nimmt alle Argumente an und reicht sie an den neuen Operator sizeof... durch
      • printCommaSeparatedList, besteht besteht aus dem Template, das ein Argument verlangt und dem, das mehr als ein Argument erwartet
        • beim Aufruf mit einem Argument printCommaSeparatedList("Only primary template used.") wird das primäre Template verwendet und der String ausgegeben
        • der Aufruf printCommaSeparatedList(42,"hello",2.3,'a') stösst das Template mit mehreren Argument an, gibt das erste Argument mit der Anweisung std::cout<<first<<","; aus, und ruft sich rekursiv mit der Anweisung printCommaSeparatedList(rest...); auf
        • diese Rekursion terminiert genau dann, wenn rest nur noch ein Element enthält, denn jetzt wird das primäre Template verwendet
    Pattern Matching und rekursiver Aufrufe sind elementare Bausteine der funktionalen Programmierung, die hier zur Anwendung kommen.

    Ein interessanteres Beispiel für Variadic Templates ist die printfFunktion. Im Gegensatz zu ihrem C Pendant ist sie typsicher.

    Konstante Ausdrücke

    • Template Metaprogramming zeichnet aus, das der Code zur Compilerzeit ausgeführt wird und somit zu Laufzeit als Konstante zur Verfügung steht
    • hierzu ist es notwendig, das die Ausdrücke zur Kompilezeit evaluiert werden können right neues Schlüsselwort constexpr
    • durch constexpr können einfache Funktionen, die einen Returnwert besitzen oder auch Objekte als konstante Ausdrücke deklariert werden
    • Beispiel:
    constexpr int square(int x) { return x * x; }

    int values[square(7)];
    • erst durch constexpr ist der Aufruf square(7) möglich

    Zusicherungen zur Kompilezeit

    • static assert erlaubt es konstante Ausdrücke ohne Template Metaprogramming Magic zur Kompilezeit zu evaluieren
    • damit können Voraussetzungen an die Templateparameter geprüft werden und gegebenfalls lesbare Fehlermeldungen ausgegeben werden
    • static_assert(sizeof(int) ==  4, "This code only works for sizeof(int) == 4") stellt sicher, das int die Länge 4 besitzt

    Weitere Features

    Standard Library

    Thread

    • C++0x besitzt eine einheitliche Threading Schnittstelle
    • die Thread Schnittstelle ist eines der letzten Featues in C++0x

    Memory Modell

    Grundproblem
    Schreibt ein Thread eine gemeinsame Variable während ein andere diese liest, ist das Verhalten nicht deterministisch.
    x=y=0

    Thread 1 Thread 2
    x=1 y=1
    r1=y r2=x

    Welchen Wert hat r1 und r2 am Ende des Programms? Können beide Werte 0 sein? right sowohl Optimierung auf Hardware Ebene (Schreibepuffer) wie auch Standard Compiler Transformationen machen dies möglich (Bruch der sequentiellen Consistenz)
    Lösung:
    1. Locks
      Thread 1                 Thread 2
      lock(l) lock(l)
      x= 1; y=1
      r1=y r2=x
      unlock(l) unlock(l)
    2. Atomics
      atomic x=y=0

      Thread 1 Thread 2
      x=1 y=1
      r1=y r2=x
    • durch atomic von x und y werden die Schreibaktionen atomar und werden sofort zwischen den Threads sichtbar
    • atomare Datentypen entsprechen im Standardfall von C++0x den volatilen Datentypen in Java
    • die C++ Datentypen volatile haben nichts mit Threading zu tun
    • das C++0x ist an das Javas Memory Modell angelehnt
    • atomare Datentypen erlauben lockfreies Programmieren
    Ein Memory Modell besteht aus:
    1. atomaren Operationen right ohne sie ist Synchronisation nahezu unmöglich
    2. partielle Ordnung von Operationen right Reihenfolge von Operationen, die der Compiler nicht verändern darf (_happens before_)
    3. Speichersichbarkeit right Zeitpunkt, ab dem der Speicher für alle Threads den gleichen Wert besitzt
    4. Data Race Semantik right Grundlage für Optimierungen des Compilers, sodass der Code seine Bedeutung behält
    persons Das Double Checked Locking Pattern ist eine optimierte Spezialform des SingletonPattern in Multithreaded Umgebungen. Es ist aber ohne eine Memory Modell nicht Threadsicher. right http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

    Lebenszeit

    • std::thread(fun) startet einen Thread, wobei func eine Funktionspointer oder ein Funktor ist
    • func kann belieg viele Argumente annehmen right CppNext#Variadic_Templates
    class Work{
    public:

    void operator()(int i,std::string s,std::vector<double> v);

    };
    std::thread t(Work(),42,"hello",std::vector<double>(23,3.141));

     

    • join lässt den Vaterthead auf die Beendigung des Threads warten, detach löst die Lebenszeit des Threads vom Vater

    Schutz von Daten

    • es gibt verschiedene Muteces
      1. nicht rekursiv (std::mutex)
      2. rekursiv (std::recursive_mutex)
      3. nicht-rekursiv mit Zeitvorgaben (std::timed_mutex)
      4. rekursiv mit Zeitvorgaben (std::recursive_timed_mutex)
    • Muteces sollten nicht direkt verwendet, sondern in std::unique_lock<> oder std::lock_guard<> gekapselt werden
    • Beispiel: einfacher Lock
    std::mutex m;
    my_class data;

    void foo(){

    std::lock_guard<std::mutex> lk(m);
    process(data);

    }
    • versuche das Lock 3 Millisekunden lang zu bekommen
    std::timed_mutex m;

    my_class data;

    void foo(){
    std::unique_lock<std::timed_mutex> lk(m,std::chrono::milliseconds(3)); // wait up to 3ms

    if(lk) process(data);
    }
    • verzöge das Locken der Ressoure und Locke alle Ressourcen gleichzeitig
    struct X{
    std::mutex m;

    int a;
    std::string b;
    };

    void foo(X& a,X& b){

    std::unique_lock<std::mutex> lock_a(a.m,std::defer_lock);

    std::unique_lock<std::mutex> lock_b(b.m,std::defer_lock);

    std::lock(lock_a,lock_b);

    // process a and b
    }
    • Vorteile
      • kapseln des Locks in std::lock_guard bzw. std::unique_lock
        right Lock wird automatisch wieder freigegeben, sobald die Lock-Wächter out of scope gehen; Anwendung des RAII Idioms
      • std::lock verhindert Deadlocks, die durch Aufrufe der Form foo(x,y) und foo(y,x) resultieren können
        right beiden Muteces werden atomar gelockt

    Initialisierung der Daten

    Das threadsicher Initialisieren von Daten ist auf mehrer Arten möglich.
    class MyClass{
    int i;
    public:
    constexpr MyClass():i(0){}

    MyClass(int i_):i(i_){}
    };

    MyClass x; // 1
    void bar(){
    static myClass z(42); // 2

    }
    MyClass* p=0;
    std::once_flag p_flag; // 3

    void create_instance(){
    p=new MyClass(43);

    }

    void baz(){
    std::call_once(p_flag,create_instance); // 3

    }
    • constexpr stellt sicher, das (1) zur Compilezeit initialisiert wird (CppNext#Konstante_Ausdrücke)
    • statische Variablen in einem Block Scope (2) werden threadsafe initialisiert
    • durch std::once_flag in Kombination mit std::call_once wird die Funktion (3) genau nur einmal ausgeführt

    Signale zwischen Threads

    • durch Bedingungsvariablen (condition variables) können Thread sich über Events synchronisieren
    std::mutex m;
    std::condition_variable cond;

    bool data_ready;

    void process_data();

    void foo(){

    std::unique_lock<std::mutex> lk(m);
    cond.wait(lk,[]{return data_ready;}); // (1)

    process_data();
    }

    void set_data_ready(){
    std::lock_guard<std::mutex> lk(m);

    data_ready=true;
    cond.notify_one(); // (2)
    }
    • cond.wait(lk,[]{return data_ready;}) hebt den Lock auf, schaut nach ob data_ready gilt und fährt gegebenfalls mit dem prozessieren der Daten fort
    • cond.notiy_one() signalisiert, das die Daten fertig sind

    Thread lokale Daten

    • durch thread_localwerden Daten definiert, die
      • dem Thread gehören
      • ihre Lebenszeit mit dem Thread teilen
      • statische Variablen in dem Sinne sind, das sie beim ersten Mal initialisiert werden
    std::string foo(std::string const& s2){
    std::thread_local std::string s="hello";

    s+=s2;
    return s;
    }
    • der String s beim ersten Durchlauf initialisiert und s2 erweitert
    • jeder weitere Durchlauf erweitert s um den Eingabeparameter s2

    Futures

    Starte eine Task(Job in einem neuen Thread), warte aber nicht auf ihren Wert, sondern hole ihn später ab
    • Future: der Thread, der den Wert abholt
    • Promise: der Thread, der den Wert liefert
    std::async
    template<class T, class V> 

    struct Accum { // simple accumulator function object
    T* b;
    T* e;

    V val;
    Accum(T* bb, T* ee, const V& v) : b{bb}, e{ee}, val{vv} {}

    V operator() () { return std::accumulate(b,e,val); }

    };

    double compute(std::vector<double>& v){

    //spawn many tasks if v is large enough
    if (v.size()<10000) return std::accumulate(v.begin(),v.end(),0.0); (1)

    auto f0 {std::async(Accum{&v[0],&v[v.size()/4],0.0})};

    auto f1 {std::async(Accum{&v[v.size()/4],&v[v.size()/2],0.0})};

    auto f2 {std::async(Accum{&v[v.size()/2],&v[v.size()*3/4],0.0})};

    auto f3 {std::async(Accum{&v[v.size()*3/4],&v[v.size()],0.0})};

    return f0.get()+f1.get()+f2.get()+f3.get();

    }
    • abhängig von der Grösse des Vektors v werden vier neue Threads gestartet und in diesen die entsprechenden Werte akkumuliert
    • die ganze Berechnung wird durch f0.get()+f1.get()+f2.get()+f3.get() synchronisiert (geblockt), den nun werden die Ergebnisse eingesammelt
    • zum jetztigen Zeitpunkt ist noch nicht klar, ob std::asyncin den aktuellen C++0x Standard aufgenommen wird
      • std::package_task verpackt eine Funktion(Funktionsobjekt) auf ähnliche Weise in einer Task
    std::promise
    • setze den Rückgabewert der Task set_value oder set_exception
    void asyncFun(std::promise<int> intPromise){

    int result;
    try {
    // calculate the result
    intPromise.set_value(result);

    } catch (MyException e) {
    intPromise.set_exception(std::copy_exception(e));

    }
    }
    std::future
    • starte die Task (2), verbinde dich mit dem Promise durch get_future (1) und hole den Wert mit get (3) ab
    std::promise<int> intPromise;
    std::unique_future<int> intFuture = intPromise.get_future(); (1)

    std::thread t(asyncFun, std::move(intPromise)); (2)

    // do some other stuff
    int result = intFuture.get(); // may throw MyException (3)
    • unique_future besitzt im Gegensatz zu shared_future Move Semantik
    Unter http://www.stdthread.co.uk/doc/headers.htmlist die Threading API schön beschrieben.

    Smart Pointer.

    Smart Pointer sind spezielle, intelligente Zeiger, die eine gewrappte Ressource mit zusätzlicher Funktionalität ausstatten.
    Die drei neuen Smart Pointer shared_ptr, weak_ptr und unique_ptr lösen die bekannten auto_ptr aus C++ ab. Sie überwachen den Lebenszyklus der Ressource nach dem RAII Idiom. Jeder dieser Smart Pointer besitzt sein spezielles Einsatzgebiet. Sowohl shared_ptr als auch insbesondere unique_ptr besitzen ein ähnliches Interface wie der auto_ptr. Der auto_ptr, der genau eine Ressource kapselt, wird als deprecated erklärt, da er zwei grosse Schwächen besitzt:
    1. beim Kopieren eines auto_ptr wird deren Inhalt mitkopiert right Transfer of Ownership oder Move Semantik
    2. er ist weder kopier- oder zuweisbar right kann nicht in Standardcontainern verwendet werden
    std::auto_ptr<int> auto1(new int(5));

    std::auto_ptr<int> auto2(auto1);
    int a= *auto1;
    • auto_ptr:
      auto_ptr.gif
    • der Aufruf int a= *auto1 ist undefiniert

    Beispiel

    Das Programm
    std::vector< std::tr1::shared_ptr<std::string> > sharedVec;

    std::tr1::shared_ptr<std::string> sharedPtr( new std::string("initial"));

    sharedVec.push_back(std::tr1::shared_ptr<std::string>( sharedPtr ));

    std::cout << "Values: sharedPtr sharedVec" << std::endl;
    std::cout << "Initial: " << " " << *sharedPtr << " " << *sharedVec[0]
    << std::endl;

    *sharedPtr="modfied";
    std::cout << "Modified: " << " " << *sharedPtr << " " << *sharedVec[0]
    << std::endl;

    std::cout << "use_count: " << sharedPtr.use_count() << std::endl;

    {
    std::tr1::shared_ptr<std::string> localSharedPtr( sharedPtr );

    std::cout << "use_count: " << sharedPtr.use_count() << std::endl;

    }
    std::cout << "use_count: " << sharedPtr.use_count() << std::endl;

    std::tr1::weak_ptr<std::string> weakPtr( sharedPtr );
    std::cout << "use_count: " << sharedPtr.use_count() << std::endl;

    std::unique_ptr<std::string> uniquePtrFirst( new std::string("only one") );

    // std::unique_ptr<std::string> uniquePtrSecond( uniquePtrFirst); will not compile
    std::unique_ptr<std::string> uniquePtrSecond( std::move(uniquePtrFirst));

    std::cout << "uniquePtrFirst.get(): " << uniquePtrFirst.get() << " *uniquePtrSecond.get(): "
    << *uniquePtrSecond.get() << std::endl;
    liefert die folgende Ausgabe:
    Values:          sharedPtr     sharedVec
    Initial: initial initial
    Modified: modfied modfied
    use_count: 2
    use_count: 3
    use_count: 2
    use_count: 2
    uniquePtrFirst.get(): 0 *uniquePtrSecond.get(): only one

    shared_ptr

    • shared_ptr:
      shared_ptr.gif
    Mehrere shared_ptr können auf eine Ressource verweisen.
    • Referenzzähler
      • inkrementiert, falls eine neuer shared_ptr auf die Ressource erzeugt wird
      • dekrementiert, falls ein shared_ptr gelöscht wird (out of scope)
      • die Ressource wird genau dann gelöscht, wenn der Referenzähler auf 0
        deterministisches Löschen der Ressource != Garbage Collection, den hier wird die Ressource nur freigegeben
    • Destruktor:
      • dem Konstruktor kann eine Funktion oder Funktor mitgegeben werden um die Ressource freizugeben
      • für den shared_ptr
        shared_ptr(Y * p, D d);
        wird im Destruktor
        d(p)
        aufgerufen
    • ist kopier- und zuweisbar right kann in Standard Containern verwendet werden
    • Schwäche:
      • zyklische Abhängigkeiten erlauben es nicht die Ressource freizugeben

    weak_ptr

    Bricht zyklische Refenzen auf.
    • bietet nur ein sehr einfaches Interface an
    • modifiziert nicht den Referenzzähler
    • kann selber keine Ressource besitzen
    • ist nicht wirklich ein Smart Pointer
    • erlaubt im Gegensatz zum shared_ptr und weak_ptr kein transparenten Zugriff auf die Ressource
    • um auf die Ressource eines weak_ptr zuzugreifen, wird diese gelockt und ein shared_ptr damit initialisiert
    std::weak_ptr<X> resource;

    if(shared_ptr<X> px = resource.lock()){
    // use *px to access the resource

    }
    else{
    // resource has expired
    }

    unique_ptr

    Besitz exclusive eine Ressource.
    • unique_ptr:
      unique_ptr.gif
    • unique_ptr ist nahezu Interfacecompatile mit dem auto_ptr Smart Pointern, unterbindet aber dessen fehlerträchtiges kopieren
    • entsprechend zum auto_ptr besitzt der unique_ptr exclusive seine Ressource, die beim Kopieren transferiert wird right Move Semantik
    • unique_ptr lassen sich aber nicht direkt kopieren, sondern müssen die Funktion std::move verwenden
    auto_ptr<int> ap1(new int);

    auto_ptr<int> ap2 = ap1; // OK, albeit unsafe
    unique_ptr<int> up1(new int);

    unique_ptr<int> up2 = up1; // compilation error: private copy constructor inaccessible
    • kann in Algorithmen verwendet werden, in den nur mit Rvalue Referenzen (temporäre Objekte; Objekte ohne Namen) oder expliziten Movekonstruktoren gearbeitet wird
    unique_ptr<int> up(new int)
    unique_ptr<int> up1= std::move(up);
    • erreicht wird dies Verhalten durch einen privaten Kopierkonstruktor und einen öffentlichen Movekonstruktor
    template <class T>
    class unique_ptr{

    public:
    unique_ptr(unique_ptr&& u); // rvalues bind here
    private:
    unique_ptr(const unique_ptr&); // lvalues bind here

    };

    Container

    Tuple

    Der neue Container Typ std::tuple ist eine Verallgemeinerung des bekannten C++ Datentyps std::pair, der Paare verschiedenes Typs binden kann.
    Tuple haben die folgenden Eigenschaften:
    • besitzen eine feste Dimension
    • kann mindestens 10 verschiedene Elemente binden
    • kann in einem Standard Container verwendet werden
    • Tupleinstanzen können genau dann miteinander verglichen werden, wenn sie einerseits die gleiche Länge besitzen und andererseits deren Elemente miteinander vergleichbar sind
    std::tr1::tuple<std::string,int,float> tup1("first tuple",3,4.17);                        (1)

    std::tr1::tuple<std::string,int,double> tup2= std::tr1::make_tuple("second tuple",4,1.1);(1)

    std::cout << std::boolalpha;
    std::cout << std::tr1::get<0>(tup1) << " and " << std::tr1::get<0>(tup2) << std::endl; (2)

    std::cout << "tup1 < tup2: " << (tup1 < tup2) << std::endl; (4)

    std::tr1::get<0>(tup2)= "Second Tuple"; (3)

    std::cout << std::tr1::get<0>(tup1) << " and " << std::tr1::get<0>(tup2) << std::endl; (2)

    std::cout << "tup1 < tup2: " << (tup1 < tup2) << std::endl; (4)
    • Tuple können sowohl durch einen Kunstruktor als auch durch die Hilfsfunktion std::tr1::make_tuple erzeugt werden (1)
    • auf einzelne Elemente kann mittels std::tr1::get<ind>(tup) lesend (2) und gegebenfalls schreibend (3) zugegriffen werden zugegriffen
    • Tuple unterstützen Vergleiche (4)
    Die Ausgabe:
    first tuple and second tuple
    tup1 < tup2: true
    first tuple and Second Tuple
    tup1 < tup2: false

    Array

    array ist ein Standard Template Library (STL) konformer Container Wrapper for Arrays fester Länge.

    Containerstatische GrösseSTL konformkontinuierlicher Speicherbereich
    C-Array DONE   DONE
    std::vector   DONE DONE
    std::array DONE DONE DONE


    Das C++0x std::array verbindet das Laufzeitverhalten des C-Arrays mit dem Interface des C++ std:vector.

    std::tr1::array <int,8> a1={1,2,3,4,5,6,7,8};                (1)

    std::tr1::array <int,8> a2={1,2,3,4,5}; (1)

    std::copy( a1.begin(), a1.end(), (2)
    std::ostream_iterator< int >(std::cout," "));

    std::cout << "\n";
    std::copy( a2.begin(), a2.end(), (2)

    std::ostream_iterator< int >(std::cout," "));
    std::cout << "\n";

    a2[7]=8; (3)
    std::copy( a2.begin(), a2.end(), (2)

    std::ostream_iterator< int >(std::cout," "));
    std::cout << "\n";

    std::cout << "std::accumulate(a2.begin(),a2.end(),0): " (2)
    << std::accumulate(a2.begin(),a2.end(),0) << "\n";

    std::cout << "a2.size(): " << a2.size() << std::endl; (2)
    • der Konstruktor von std::array verlangt ein Aggregat; fehlende Werte werden Defaultinitialisiert (1)
    • std::array kann in den Algorithmen der STL verwendet werden (2)
    • als Array erlaubt es natürlich den Indexzugriff (3)
    Die Ausgabe:
    1 2 3 4 5 6 7 8
    1 2 3 4 5 0 0 0
    1 2 3 4 5 0 0 8
    std::accumulate(a2.begin(),a2.end(),0): 23
    a2.size(): 8

    Ungeordnete Assoziative Container (Hashtabelle)

    Die Ungeordneten Assoziativen Container verhalten sich wie die bekannten Geordneten Assoziativen Container.
    Der feine Unterschied ist
    1. die Schlüssel der ungeordneten Datentypen sind nicht geordnet right konstante Zugriffszeit ist möglich
    2. die geordneten Datentypen haben logarithmische Zugriffszeit
    3. die Elemente der Ungeordneten Assoziativen Container müssen nicht vergleichbar sein
    Die Hashtabelle schafften es nicht mehr in in C++98 Standard right viele Compilerbauer boten ihre eigene Erweiterungen an rightNamen wie hash_map waren schon vergeben
    • es gibt vier verschiedene Ungeordnete Assoziative Arrays
      • unordered_set: eindeutige Schlüssel
      • unordered_multiset: mehrere gleiche Schlüssel möglich
      • unordered_map: nur ein (Schlüssel,Wert) Paar mit gleichem Schlüssel erlaubt
      • unordered_multimap: mehrere (Schlüssel,Wert) Paare mit gleichem Schlüssel möglich
    • jedem Ungeordneten Assoziativen Array steht aus C++98 ein Geordnetes Assoziatives Array gegenüber
      Ungeordnete Assoziative ArraysGeordnete Assoziative Arrays
      std::unordered_set std::set
      std::unordered_multiset std::multiset
      std::unordered_map std::map
      std::unordered_multimap std::map
    • die Anwendung der Ungeordneten Assoziativen Arrays ist ziemlich unspektakulär, da dieser der der Geordneten Assoziativen Arrays entspricht
    std::map<std::string,int> m { {"Dijkstra",1972},{"Scott",1976},{"Wilkes",1967},{"Hamming",1968} };

    m["Ritchie"] = 1983;
    for(auto x : m) std::cout << '{' << x.first << ',' << x.second << '}';

    std::unordered_map<std::string,int> um { {"Dijkstra",1972},{"Scott",1976},{"Wilkes",1967},{"Hamming",1968} };

    um["Ritchie"] = 1983;
    for(auto x : um) std::cout << '{' << x.first << ',' << x.second << '}';

    Reguläre Ausdrücke

    Der Umgang mit regulären Ausdrücken in C++0x lässt sich in drei Schritte zerlegen.

    std::regex rgx(R"\d+");                                           (1)

    std::smatch match; (2)
    if (std::regex_search(std::string("123A43"), match, rgx)) (3)

    std::cout << "match found after " << match.prefix() << '\n';
    1. std::regex rgx(R"\d+")hält den regulären Ausdruck
      • R"\d+" bezeichnet einen Raw String Literal in C++0x
      • der String folgt per Default der ECMAScript Grammatik, aber auch POSIX BRE, ERE, awk, grep, egrep oder sed Syntax ist möglich
      • neben std::regex gibts auch std::wregex für wide characters wchar_t
    2. std::smatch matcherhält das Ergebnis der Suche
      • match[0]: der Gesamtmatch
      • match[i]: i>0 sind die Teilmatches
      • position , suffix , prefix und length liefern weiter Informationen
    3. std::regex_searchverarbeitet das Suchergebniss weiter
      • std::regex_match: verlangt einen genauen Treffer und gibt ein Boolean zurück
        std::regex_match("bd",std::regex(R"b(c*)d"))
      • std::regex_search: schaut nach dem ersten geeignete Sequenz
      • std::regex_replace: schaut nach einem Treffer und ersetzt ihn

    Wiederholtes Suchen

    Mit std::regex_iterator und std::regex_token_iteratorlässt sich ein Iterator über eine Eingabesequenz definieren.
    • std::regex_iterator: liefert die Zeichen, die dem regulären Ausdruck entsprechen
    • std::regex_token_iterator: splittet die Eingabesequenz mit Hilfe der regulären Ausdrücke right Tokenizer
    std::cout << std::endl;
    boost::regex reg1("[^13579,]");

    std::string str1="1,2,3,4,5,6,7,8,9,10,11,12";
    boost::sregex_iterator it1(str1.begin(),str1.end(),reg1);

    boost::sregex_iterator end1;
    std::cout << "Character Stream: " << std::endl;

    while (it1 != end1) std::cout << " " << *it1++;

    std::cout << "\n\n\n";

    boost::regex reg2(",");

    std::string str2="1,2,3,4,5,6,7,8,9,10,11,12";
    boost::sregex_token_iterator it2(str2.begin(),str2.end(),reg2,-1);

    boost::sregex_token_iterator end2;
    std::cout << "Token Stream: " << std::endl;

    while (it2 != end2) std::cout << " " << *it2++;

    std::cout << "\n";
    std::cout << std::endl << std::endl;
    • it1(str1.begin(),str1.end(),reg1) gibt einen Iterator über alle Zahlen aus "1,2,3,4,5,6,7,8,9,10,11,12" zurück, die nicht in reg1("[^13579,]") sind
    • it2(str2.begin(),str2.end(),reg2,-1) gibt einen Iterator über die kommaseparierten reg2(",") Tokens des Strings "1,2,3,4,5,6,7,8,9,10,11,12" zurück
    • Ausgabe:
      Character Stream:
      0 2 4 6 8 0 2

      Token Stream:
      1 2 3 4 5 6 7 8 9 10 11 12

    Zufallszahlen

    Zufallszahlen sind in vielen Bereichen notwendig:
    • Tests
    • Spiele
    • Simulationen
    • Sicherheit
    Der Zufallszahlengenerator (std::variate_generator) besteht aus zwei Teilen. Einem Generator, der Sequenzen von Zufallszahlen erzeugt und einer Verteilung, die die Zufallszahlen in einem vorgegebenem Bereich verteilt.

    std::tr1::mt19937 rng;                     // generator

    std::tr1::uniform_int<> six(1,6); // distribution
    std::tr1::variate_generator<std::tr1::mt19937, std::tr1::uniform_int<> > dice(rng, six);

    for ( int i=1; i<= 9; ++i){

    std::cout << "dice["<< i << "]: " << dice() << std::endl;

    }
    • Ausgabe
      dice[1]: 5
      dice[2]: 1
      dice[3]: 6
      dice[4]: 6
      dice[5]: 1
      dice[6]: 6
      dice[7]: 6
      dice[8]: 2
      dice[9]: 4
    Ein Überblick über verschiedene Generatoren und Verteilungen ist unter http://www.boost.org/doc/libs/1_41_0/libs/random/index.html. Dies entspricht annähernd der Funktionalität von C++0x.

    Type Traits

    Ein Program, das ein anderes Program erzeugt, nennt sich Metaprogramm. Tut sie dies noch zur Compilezeit mit Templates, dann heißt diese Technik Static Template Metaprogramming. Die type_traits Bibliothek erlaubt Typabfragen, Vergleiche und Modifikationen zur Compilezeit. Damit lassen sich Algorithmen speziell auf den Datentyp zuschneiden und doch generisch aufrufen.

    #include <tr1/type_traits>
    #include <iostream>

    template <class Ty>
    void multBy3Impl(Ty val, const std::tr1::true_type&){

    std::cout << "Arithmetic type: ";
    std::cout << 3*val << std::endl;

    }

    template <class Ty>
    void multBy3Impl(Ty val, const std::tr1::false_type&){

    std::cout << "No arithmetic type: ";
    std::cout << val << val << val << std::endl;

    }

    template <class Ty>
    void multBy3(Ty val){

    multBy3Impl(val, std::tr1::is_arithmetic<Ty>());
    }

    int main(){
    multBy3(1);
    multBy3(5.5);

    multBy3(std::string("Only for testing purpose. "));
    }
    • das Programm evaluiert zu Compilezeit, ob das Argument von multBy3 ein arithmetischer Typ std::tr1::is_arithmetic ist oder nicht
    • abhängig von dem Ergegnis wird multBy3Impl(Ty val, const std::tr1::true_type&) bzw. multBy3Impl(Ty val, const std::tr1::false_type&) aufgerufen
    • die entsprechende Implementierung multBy3Impl weiß, wie sie den Datentyp zu multiplizieren hat
    • die Ausgabe zeigt, das der Typdispatch zur Compilezeit richtig ausgeführt wird
      Arithmetic type: 3
      Arithmetic type: 16.5
      No arithmetic type: Only for testing purpose. Only for testing purpose.
      Only for testing purpose.
    Typ Abfragen
    • Primary Type Categories ( is_void, is_floating_point, is_array... )
    • Composite Type Categories ( is_arithmetic, is_object, is_member_pointer... )
    • Type Properties ( is_const, is_pod, is_abstract,... )
    Typ Vergleiche
    • Type Relationships ( is_same, is_convertible, is_base_of... )
    Type Modfikationen
    • Typ Transformations ( remove_const, add_const, remove_reference... )
    • Alignment ( alignment_of, aligned_storage )
    Der ganze Rest an Funktionen kann unter http://msdn.microsoft.com/en-us/library/bb982077.aspxnachgelesen werden.
    Optimiertes Kopieren von Datenstrukturen

    Ein generisches Kopieralgorithmus für Standardcontainer kann so aussehen:

    template<typename I1, typename I2, bool b

    I2 copy_imp(I1 first, I1 last, I2 out, const std::tr1::integral_constant<bool, b>&){

    while(first != last){
    *out = *first;

    ++out;
    ++first;
    }
    return out;

    }
    Jedes Element des Bereiches [first,last[ wird sukzessive an den Bereich [out,...[ kopiert.
    Dies geht mit memcpy deutlich schneller.

    template<typename T>                                                            
    T* copy_imp(const T* first, const T* last, T* out, const std::tr1::true_type&){
    memcpy(out, first, (last-first)*sizeof(T));

    return out+(last-first);
    }
    Die Datenstrukturen müssen aber hinreichend einfach sein um memcpyzu verwenden:
    • die Iteratoren müssen Pointer sein ( const T* first, const T* last, T* out )
    • die Iteratoren müssen auf die gleichen Typen verweisen ( <typename T> enthält nur ein Typ)
    • die Elemente des Containers müssen einen trivialen, vom Compiler automatisch erzeugten, Zuweisungsoperator besitzen ( const std::tr1::true_type& )
    Ein Aufruf der Form
    template<typename I1, typename I2>
    I2 copy(I1 first, I1 last, I2 out){

    typedef typename std::iterator_traits<I1>::value_type value_type;
    return copy_imp(first, last, out, std::tr1::has_trivial_assign<value_type>());

    }
    • stösst die richtige copy_imp Implementierung an
    • durch typedef typename std::iterator_traits<I1>::value_type value_type wird der Typ der Containerelemente bestimmt um ihn anschliessend in std::tr1::has_trivial_assign<value_type> zu nutzen
    • abhängig vom Ergebniss der Evaluierung wird der optimierte oder generische Kopieralgorithmus verwendet
    • das Ergebnis des Aufrufs std::tr1::has_trivial_assign<value_type> ist eine Klasse, so dass der Dispatch auf das vierte Argument der copy_imp Methoden vollzogen wird
    • der Ausdruck typedef integral_constant<bool, true> true_type; definiert eine Spezialisierung true_type von std::tr1::integral_constant<bool, b>

     

    Referenz Wrapper

    Referenzen sind in C++ keine First class objects.
        right sie können nicht kopiert werden
        right sie können nicht in Standardcontainern verwendet werden, da sie hierzu copy-constructible and assignable sein müssen
        right dieser Ausdruck lässt sich in C++ nicht kompilieren: std::vector<int&>
    Die C++0x Bibliothek reference_wrapper erzeugt aus Objekten vom Type T& copy-constructible and assignableObjekte. Damit lassen sich
    • Objekte in Containern verwenden, die sich nicht Kopierkonstruierbar und Zuweisbar sind (Dateien, Locks oder auch Netzwerkverbindungen)
    • Objekte mit Referenzsemantik in Containern verwenden
    reference_wrapper ist ein copy-constructible and assignable Wrapper um das Objekt vom Typ T&.

    std::vector<bool> copyVec;
    std::vector< std::tr1::reference_wrapper<bool> > refVec;

    bool b=false;
    copyVec.push_back(b);
    refVec.push_back(std::tr1::ref(b));

    std::cout << std::boolalpha;
    std::cout << "Values: b copyVec refVec" << std::endl;

    std::cout << "Initial: " << b << " " << copyVec[0] << " " << refVec[0] << std::endl;

    b= true;
    std::cout << "Modified: " << b << " " << copyVec[0] << " " << refVec[0] << std::endl;
    right refVec[0] ist eine Referenz auf b und wird durch b= true modifiziert
    • Ausgabe:
      Values:      b       copyVec    refVec
      Initial: false false false
      Modified: true false true
    Die Hilfsfunktion
    • ref<T> gibt einen T& Wrapper Objekt
    • cref<T> gibt einen const T& Wrapper Objekt
    zurück.
    template <typename T>

    void doubleMe(T t){
    ++t;
    };

    int f=1;
    std::cout << "initial value: " << f << std::endl;

    doubleMe(f);
    std::cout << "doubleMe(f): " << f << std::endl;

    doubleMe(std::tr1::ref(f));
    std::cout << "doubleMe(ref(f)) : " << f << std::endl;
    • Ausgabe:
      initial value:     1
      doubleMe(f): 1
      doubleMe(ref(f)) : 2

    bind und function

    Die beiden Librarys bind und functionergänzen sich sehr gut.
    • bind
      • erlaubt Argumente an beliebige Positionen einer Funktion, Methode oder eines Funktionsobjektes zu binden
      • das resultierende Funktionsobjekt kann direkt aufgerufen, in den Algorithmen der Standard Template Library verwendet oder in einem Funktionsobjekt function gespeichert werden
      • für Argumente ohne Wert können Platzhalter eingeführt werden
    • function
      • ermöglicht die einfache Definition von Funktionsobjekten
      • diese Funktionsobjekten sind first class functions
      • erlauben die einfache Definition von Callbacks
    • bind und function
      • teilweise evaluieren jedes beliebigen Arguments mit bind und binden der neuen Funktion mit function right sehr mächtiges currying lässt sich umsetzen
      • Funktionsobjekte, die mit bind erzeugt werden, können direkt mit function gebunden und später aufgerufen werden.
    Die Funktionalität von bind kann oft durch lambda Funktionen ausgedrückt, die von function wird durch auto angeboten.

    double divMe(double a, double b){

    return double(a/b);
    }


    template <typename T>

    T addMe(T a, T b){
    return a+b;

    };

    int main(){

    std::cout<< "1/2.0= " << bind(divMe,1,2.0)() << std::endl; (1)

    function<double(double)> myDiv= bind(divMe,_1,2.0); (2)

    std::cout<< "1/2.0= " << myDiv(1) << std::endl;

    function<int(int)> addInt = bind(addMe<int>, _1, 2);

    std::cout << "1+2= " << addInt(1) << std::endl;

    function<std::string(std::string)> addString = bind(addMe<std::string>,"first",_1); (3)

    std::cout << "first + second= " << addString("second") << std::endl;

    auto funcObject1= bind(addMe<std::string>, "first",_1);

    std::cout << "first + second= " << funcObject1("second") << std::endl;

    auto funcObject2= bind(addMe<std::string>, _1, _2);

    std::cout << "first + second= " << funcObject2("first","second") << std::endl;

    std::vector<int> myVec{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};

    std::copy_if( myVec.begin(), myVec.end(), (4)

    std::ostream_iterator<int>( std::cout, ", " ),
    bind( std::logical_and<bool>(),

    bind( std::greater <int>(),_1,9 ),
    bind( std::less <int>(),_1,16 )));

    std::cout<< std::endl;

    /* compiles only with gcc 4.5 (5)
    std::copy_if( myVec.begin(), myVec.end(),

    std::ostream_iterator<int>( std::cout, ", " ),
    [](int a){ return (a>9)&&(a<16);});
    std::cout << std::endl;
    */

    }
    1. bind(divMe,1,2.0)() definiert einen Funktionskörper, bindet die Argumente 1 und 2.0 und ruft ihn auf
    2. bind(divMe,_1,2.0) bindet das zweite Argument mit 2.0, führt eine Platzhalter _1 für das erste Argument ein, bindet das Funktionsobjekt an function<double(double)> und führt es anschliessend mit myDiv(1) aus
    3. auto funcObject1 erleichert die Definition von Funktionsobjekten
    4. zuviele geschachtelte bind Ausdrücke (ich habe die ganzen Namespaces der Lesbarkeit wegen bekannt gemacht), können verwirrend wirken
    5. diese Funktion bietet die gleiche Funktionalität wie (4) an
    Ausgabe:
    1/2.0= 0.5
    1/2.0= 0.5
    1+2= 3
    first + second= firstsecond
    first + second= firstsecond
    first + second= firstsecond
    10, 11, 12, 13, 14, 15,

    Weitere Bibliotheken

    • time utilities
    • return Wert Evaluierung von Funktionsobjekten mit return_of; die Funktonalität wird schon durch die Alternative Funktionssyntax mit decltype angeboten
    • einfache Aufrufwrapper mit mem_fn; die Funktionalität wird schon durch std::bind angeboten

    Compilerunterstützung

    Weitere Informationen

    Verarbschiedung des Standards

    Mail am 15.03.2010 an comp.lang.c++.moderated von Herb Sutter.
    • March 2010 ISO C++ standards meeting
        ...
      1. Approved Final Committee Draft (FCD) for C++0x

      The biggest news is that this afternoon we voted in the final
      remaining feature changes to C++0x, and to much applause then
      unanimously approved the text for international ballot as a Final
      Committee Draft (FCD). FCD means that, assuming no surprises, we
      intend to do only bug fixes and editorial corrections for the next
      year or so, and then ballot a final standard. If we can do that,
      assuming all goes well, C++0x could officially be published as soon as
      next year as ISO C++ 2011, and we can stop with the "x-is-hex" jokes
      and just start calling it C++11.

      This is a big milestone, and it was achieved thanks to removing a
      couple of controversial features last summer and a whole lot of work
      by the ISO C++ committee members over the past six months in
      particular. That work includes countless hours spent between our full
      face-to-face meetings at face-to-face ad-hoc meetings to swat library
      bugs, teleconferences on resolving core language questions, and
      triple-
      digit person-hours invested in four teleconferences during December-
      February purely about C and C++ compatibility that have greatly helped
      to identify and attempt to resolve minor areas of divergence between
      the C++0x draft standard and the C1x draft standard (as both are now
      in progress; C1x is targeting completion and publication in 2012).

      All in all, your committee members have put in an enormous amount of
      effort to bring this in, and the draft is in far better shape for this
      meeting than anyone could have expected last summer. For comparison,
      in my and several others'; opinions, it's in better shape than the FCD
      of the C++98 standard.
      ...

     

  •  

    MapReduce

    Einführung

    MapReduce
    MapReduce ist ein von Google Inc. eingeführtes Framework für nebenläufige Berechnungen über große (mehrere Petabyte) Datenmengen auf Computerclustern. (DeWikipedia:MapReduce)
    • es ist
      fast
      robust
      easy to use
      scalable
      widely applicable
      Monitoring
    • das Framework ist seit 2003 bei Google zunehmend mehr im Einsatz

    Namensgebung:

    • viele konkrete Probleme können in den Funktionen höherer Ordnung map und reduce ausgedrückt werden right MapReduce ist ein Framework, das über die zwei Funktionen map und reduce durch den Anwender parametrisiert werden kann.
    • map und reduce Beispiele in python
    >>> map(lambda x: x*x,range(1,11))

    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

    >>> map(lambda a: ord(a),"aeioAEIOU")
    [97, 101, 105, 111, 117, 65, 69, 73, 79, 85]

    >>> reduce(lambda a,b:a*b,range(1,11))

    3628800
    >>> reduce(lambda a,b:a+" "+b,["Only","for","testing","purpose","."])

    'Only for testing purpose .'

    Grundidee

    1. biete ein mächtiges Framework mit den folgenden Eigenschaften an
      • automatische Parallelisierung, Koordininierung und Verteilung von Jobs
      • Fehlertoleranz gegenüber Hardware- und Softwareausfällen
      • automatische Lastverteilung
      • Optimierung von Netzwerk und Datentransfer
      • Überblick mittels Status und Monitoring
    2. lasse den Benutzer die Anwendung des Framework über die zwei Funktionen map und reduce parametrisieren
    right Framework, das für viele Einsatzzwecke verwendet werden kann
    Anwender beschäftigt sich nur noch mit der Anwendungslogik

    Anwendungen

    • Indizierung grosser Datenmengen (grösser als 20 Terabyte) für die Suchaufbereitung
      • mit mehreren MapReduce Berechnungen werden die Daten strukturiert
    • Maschinelles Lernen
      • künstliches Erzeugung von Wissen aus Erfahrung
    • Verteiltes
      • Suchen von Pattern
      • Sortieren von Daten
      • Rechnen (Matrizenmultiplikationen)
    • Auswertung von Log Dateien
    • Kategorisieren von Daten
      • Nachrichtenaufbereitung
    • Datenaufbereitung um Reports für häufige Anfrage zu erzeugen
    • Erzeugen von Graphen
      • Aufrufhäufigkeit von Seiten
      • Verlinkung von Webseiten
    • Extrahieren von Daten und neue Einsichten zu erlangen

    Protokoll

    Datenfluß

    • Datenfluß MapReduce:
      Dataflow.gif
    • die Eingabedatei wird auf mehrere map-Prozesse verteilt und die map-Phase beginnt
    • die map-Prozesse berechnen parallel die Zwischenergebnisse
    • sobald alle map-Prozesse fertig sind, beginnt die reduce-Phase
    • die reduce-Prozesse berechnen parallel für bestimmte Zwischenergebnisse die Ausgabedaten
    • für jeden reduce-Prozess wird eine Ausgabedatei erzeugt
    • sind alle reduce-Prozesse fertig, ist die reduce-Phase vollendet und somit auch die MapReduce Berechnung

    Details

    Mapper

    • Deklaration in Haskell
       map: (s1,w1)->[(s2,w2)] 
    • verarbeitet eine Liste von (Schlüssel,Wert) Paaren (s1,w1)
    • erzeugt eine Liste von (neuen Schlüssel,Wert) Paaren (s2,w2) aus jedem Eingabepaar (s1,w1)
      • die Liste kann auch leer sein
      • der Mapper ist implizit gegebenfalls auch ein Filter
    • der Mapper ist mit der Verarbeitung der Liste aller Paare (s1,w1) fertig

    Reducer

    • Deklaration in Haskell
      reduce: (s2,[w2]) -> [w3] 
    • erhält eine alle Paare mit gleichem Schlüssel s2 in der Form des Paares (s2,w2*)
    • wendet reduce auf die Werte w2 an und gibt eine Liste von w3's als Ergebnis aus

    map und reduce Besonderheiten

    Die Begrifflichkeit von MapReduce lehnt sich nur an ihre funktionalen map und reduce Pendants an (Vgl.: Google's MapReduce Programming Model -- Revisited) . Die map Funktion übernimmt implizit die Funktionalität der dritten elementaren funktionalen Funktion filter.
    GHCi, version 6.10.2: http://www.haskell.org/ghc/  :? for help
    Loading package ghc-prim... linking... done.
    Loading package integer... linking... done.
    Loading package base... linking... done.
    Prelude> :type map
    map :: (a -> b) -> [a] -> [b]
    Prelude> :type foldl
    foldl :: (a -> b -> a) -> a -> [b] -> a

    • die map und reduce Funktion aus MapReduce entspricht der Funktionen in der funktionalen Welt, die auf die Liste angewandt wird
      Eigenschaftmap funktionalmap Phase MapReduce
      Eingabe Liste von Werten Listen von Paare (Schlüssel,Wert)
      Verarbeitung jeder Wert wird auf einen neuen Wert abgebildet (Schlüssel,Wert) Paar kann auf keine oder mehrere (Schlüssel,Wert) Paare abgebildet werden
      Ausgabe Liste von neuen Werten Liste von neuen Paaren (Schlüssel,Liste von Werten)
    Die map und reduce Funktionen in MapReduce stellen nur die verarbeitenden Schritte in der map und reduce Phase dar. Sie sind die Funktionen, die auf den Eingabestrom angewandt werden.

    Beispiele

    • Klassiker: Worthäufigkeit in einer Datei zählen
      • map Funktion erhält als Input die Liste aller Paare (Zeilennummer,Zeileninhalt)
      • map Funktion produziert zu jedem Paar (Zeilennummer,Zeileninhalt) alle Paare (Wort,1) zu jedem Wort der Zeile
      • reduce Funktion erhält zu jedem Wort eine Paar (Wort,[1,..,1]) und berechnet die Häufigkeit von Wort in der Datei, indem es die 1 in [1,..,1] addiert
      • als Ergebnis steht eine Liste [(Wort,Häufigkeit des Wortes)] über alle Wörter aus der Datei zu Verfügung
    • weitere Anwendungsfälle
      AnwendungEingabe für MapperAusgabe MapperEingabe ReducerAusgabe Reducer
      Wortsuche in mehreren Dateien [(Dateiname,Dateiinhalt)] [(wort,(Dateiname,Zeilenummer)] [(wort,[(Dateiname,Zeilenummer),...,(Dateiname,Zeilenummer)] [(wort,[(Dateiname,Zeile),...,(Dateiname,Zeile)]
      Am häufigsten referenzierte Web-Pages ermitteln [(url,Inhalt der html-Seite)] [(Referenz einer url,1)] [(Referenz einer url,[1,..,1] ) [(Referenz einer url, Länge von ([1,..,1])]
      Verteiltes Sortieren (Wörter) [(i-ter Teil ,Wörter des i-ten Teils)] ([Buchstabe == k,( k-Wort )] [Buchstabe k,(Liste über k-Wörter)] [(Buchstabe, sortierte Liste über k-Wörter)]
      Primzahlen aus n-Zahlen ermitteln [(i-ter Teil, Zahlen des i-ten Teils)] [(Zahlenbereich i,Zahl im Zahlenbereich i)] [Zahlenbereich,(alle Zahlen aus Zahlenbereich i)] [Zahlenbereich i, (Primzahlen aus Zahlenbereich)]

    Mitwirkende

    • der Eingabe Leser
      • teilt die Eingabe in große Blöcke (64MB Google) und weist in einem Mapper zu
      • transformiert die Blöcke in (Schlüssel,Wert) Paare für den Mapper
    • die Mapper Funktion
      • nimmt die Liste von (Schlüssel,Wert) Paare and und erzeugt zu jedem Paar eine neue Listen von (Schlüssel,Wert) Paaren, die auch leer sein kann
    • die Verteilerfunktion
      • die Ausgabe aller Maps wird den entsprechenden Reducer zur Weiterverarbeitung zugewiesen
      • eine typische Zuweisung erfolgt durch den Hashwert des Keys modulo der Anzahl der Reducer
    • die Vergleichsfunktion
      • sortiert die Werte der Mapper Funktion entsprechend der Schlüssel
      • dies ist notwendig, da der Reducer die Werte mehrerer Schlüssel in der Regel bearbeitet
    • die Reducer Funktion
      • erzeugt zu jedem Schlüssel und deren Werte einen Ausgabeliste, die auch leer sein kann
    • der Ausgabe Schreiber
      • speichert das Ergebnis in einer Datei ab

    Architektur

    Ausführung

    • MapReduce Framework:
      Architecture.gif
    1. das MapReduceFramework
      • teilt die Eingaben in m Teile auf
      • start die Master und Worker Prozesse auf dem Cluster
    2. Master
      • weist die m map Aufgaben und r reduce Aufgaben den entsprechenden Rechner zu
    3. Mapper
      • lesen die Wert als (Schlüssel,Wert) Paare ein
      • verarbeiten die Daten
      • schreiben die Zwischenergebnisse in den Speicher
    4. Mapper
      • schreiben die Zwischenergebnisse periodisch auf die Platte
      • die Zwischenergebnisse werden in r Dateien aufgeteilt
      • teilt dem Master die Namen der Dateien mit
    5. Reducer
      • bekommt die Adressen der Zwischenergebnisse mitgeteilt
      • liest alle für ihn bestimmten (Schlüssel,Wert) Paare ein
      • sortiert die (Schlüssel,Wert) Paare nach dem Schlüssel
    6. Reducer
      • iteriert über die (Schlüssel,Wert) Paare für jeden Schlüssel
      • übergibt die (Schlüssel,Wert) Paare an die reduce Funktion
      • die reduce Funktion akkumuliert die Ergebnisse des reduce Schrittes zu einem Ausgabewert
    7. das MapReduce Framework
      • warten bis alle map und reduce Prozesse fertig sind
      • übergibt die Kontrolle wieder der Applikation

    IO Managment

    • Kompression der Daten
    • Serialisierung der Daten
    • Verteilung und Sortierung der Daten
    • Verwaltung der Metadaten auf dem Master

    verteilte Dateisystem

    • Charakteristiken
      • sehr große Datenmengen Petabytes
      • wenige grosse Dateien (>GB) bestehen aus Datenblöcken, die typischerweise 64 MB groß sind
      • die Datenblöcke liegen redundant (typischerweise 3 mal) auf Chunkservern vor
      • Datendurchsatz ist wichtiger als die Zugriffszeit
      • Streaming Zugriff auf die Daten
        • lesen vom Dateianfang
        • schreiben ans Dateiende
      • write-once, read-many-times Zugriffe
      • wenige grosse, statt vieler kleiner Zugriffe
        • meist werden die ganzen Daten gelesen
      • kann mit hohen Fehlerraten umgehen (Chunkserver sind einfache PCs)
    • Beispiele

    Status und Monitoring

    Mittels eines Webservers kann das MapReduce Framework administriert werden.
    • Überblick über die Master und Worker
    • Status und Fortschritt der einzelnen Jobs
      • Zähler über die Map Prozesse
    • Zugriff auf die Logdateien

    Tiefere Einsichten

    • Master
      • überwacht den Zustand der Arbeiter (idle, in-progress,completed)
      • kennt die r Zwischenergebnisse Adressen jedes Mappers
    • Fehlertoleranz
      • Arbeiter
        • Master pingt in periodischen Zeitabschnitten die Arbeiter
        • Ausfall einer map Aufgabe
          • werden neu auf andere Rechner verteilt, da sich die Zwischenergebnisse im Arbeitsspeicher oder lokalen Speicher des Rechners befinden
          • die reduce Aufgaben werden wiederholt
        • Ausfall einer reduce Aufgaben
          • fertige reduce Aufgaben sind durch das globalen Filesystem weiter verfügbar
      • Master
        • bei einem Fehler des Master wird MapReduce neu angestossen
      • Umgang mit mehrfachen Ergebnissen
        • wird eine map Aufgabe von mehreren Mapper gleichzeitig gelöst, verwendet der Master das Ergebnis des ersten Mappers
        • Reducer, die die gleiche Aufgabe lösen, überschreiben ihre Fehler im globalen Filesystem
    • Datenlokalität
      • die Rechner werden zu Mappern erklärt, die den Eingabedaten am nächsten sind
        • Entscheidunskriterium ( Knoten, Rack, Datenzenter )
      • im Google File Sytem (GFS) und Hadoop File System (HDFS) sind die Datenblöcke mindenstens 3 mal vorhanden
    • Aufgabenaufteilung
      • die Anzahl m der map und die Anzahl r der reduce Aufgaben sollte deutlich höher als die Anzahl der Rechner sein, damit Aufgaben optimal verteilt werden können
      • typische Werte von Google für 2000 Arbeiter
        • map Aufgaben über 16 - 64MB
        • 20000 map Aufgaben
        • 5000 reduce Aufgaben
    • Nachzügler
      • kurz vor Ende des MapReduce Prozesses werden neue Worker für die nicht vollendeten Aufgaben gestartet
      • sobald der ursprüngliche Worker oder der neue Worker fertig, ist dieser Teil des MapReduce Prozesses fertig

    Erweiterungen

    • die Partitionsierungsfunktion kann vorgegeben werden
      • Default: hash(key) mod r
    • die (Schlüssel,Wert) Paare einer Partition sind nach dem Schlüssel aufsteigend sortiert
    • Combiner Funktion
      • die Zwischenergebnisse der Mapper können schon vorab mit dem Reducer vereinfacht werden, wenn dieser asssoziativ und kommunitativ ist
      • im klassischen Worthäufigkeit Zähler Problem ermittelt der Mapper statt k Paare (wort,1), ein Paar (wort,k) zu einem gegebenen Wort
    • lesen der Eingabe und schreiben der Ausgabe
      • damit der Mapper die Daten in der richtigen Form (Liste über (Schlüssel,Wert)) erhält, kann eine Eingabefunktion dem Mapper vorangestellt werden
      • die Ausgabefunktion lässt sich ebenfalls parametrisieren, sodass mehrere MapReduce Berechnungen verkettet werden können right die Komposition von mehrern MapReduce Jobs ist einem komplexen MapReduce Job vorzuziehen
    • Umgang mit fehlerhaften Datensätzen
      • fehlerhafte Datensätze, die die Worker zum Abbruch zwingen, werden vom Master ausgesondert
    • debugging Unterstützung
      • eine lokale, sequentielle MapReduce Implementierung hilft bei der Fehlersuche

    Performance

    • Beispiel von Google (http://labs.google.com/papers/mapreduce.html)
      • 3 Zeichen grep über 1 Terabyte Daten
      • 1800 Worker mit 4 GByte Ram, 2GHz Intel Xeon Prozessor mit Hyperthreading und 2 160 GB IDE Festplatten
      • das gesuchte Pattern existiert 92.000 mal
      • right 15000 map Aufgaben und 1 reduce Aufgabe benötigten 150 Sekunden (60 Sekunden startup)
    • Eckdaten
      • Wer kann am schnellsten 1 Terabyte Daten sortieren?
        • Google und Yahoo schaffen dies mit Clustern von 3000 - 4000 Rechner in ca. 60 Sekunden
      • MapReduce skaliert linear
      • Datengrösse Petabyte (10^15 Byte oder auch 1 Million Gigabyte)

    Implementierungen

    • Google
      • Framework in C++
      • Interface in Python und Java google-mapreduce-chart.png
    • Hadoop(Yahoo)
      • Java
      • Interface in Java
      • spezielles C++ Interface über Hadoop Pipes (Sockets)
      • streaming fähig
      • im Einsatz bei
        • Yahoo
        • Amazon
        • AOL
        • Facebook
        • IBM
        • The New York Times
    • disco(Nokia)
      • Framework in Erlang
      • Interface in Python

    Beispiele

    • erster Versuch mit disco scheiterte; Jobs wurden einfach nicht an die Worker geschickt
    • zweiter Versucht mit dem Streaming Interface Hadoop klappt sofort, das nur ein Java 1.6 notwendig war

    Hadoop

    setup

    streaming Interface

    • mapper: Mapper Funktion
    • reducer: Reducer Funktion
    • input: Eingabedatei(en)
    • output: Ausgabeverzeichnis

    Codebeispiele

    • zwei Funktion readInput und outputWriter, die die Eingabe von stdin lesen und nach stdout schreiben
    import sys

    def readInput(mapper):
    for (num,line) in enumerate(sys.stdin):

    line=line.strip()
    mapper(num,line)


    def outputWriter(reducer,sortKey=None):
    key2Values={}

    for line in sys.stdin:
    line= line.strip()

    key,value= line.split()
    try:
    key2Values[key].append(value)

    except:
    key2Values[key]=[value]
    results=[]

    for key in key2Values.keys():
    results.append((key,reducer(key,key2Values[key])))

    results.sort(key=sortKey)
    for (key,value)in results:

    print "%s %s"% (key,value)
    • readInput
      • wird über den Mapper parametrisiert
      • nimmt den Eingabestrom an
      • erzeugt für jedes Zeile ein Paar (Zeilennummer,Zeile)
      • füttert mit dem Paar (Zeilennummer,Zeile) den Mapper
      • erzeugt aus dem Eingabestrom einen Ausgabestrom (Schlüssel,Wert) und füttert damit den Mapper
    • outputWriter
      • wird über den Reducer und eine Sortierfunktion parametrisiert
      • nimmt den Eingabestrom an
      • baut für jeden Schlüssel die Lister aller Werte auf
      • wendet die reduce Funktion auf die Elemente dieser Liste an
      • sortiert die Ergebnisliste gegebenfalls
      • schreibt das Ergebnis aus stdout

    MapReduce Aufrufe

    Worthäufigkeit
    • bestimme zu jedem Wort seine Häufigkeit nach Wörter sortiert
    • Aufruf
      $HD/bin/hadoop jar $HD/contrib/streaming/hadoop-0.20.1-streaming.jar -mapper $HD_W/mapperWordCount.py
      -reducer $HD_W/reducerWordCount.py -input $HD_W/book/grimm.txt -output $HD_W/grimmWordCount.out
    • Mapper
    #!/usr/bin/env python

    import ioMapReduce

    def mapper(key,value):

    words = value.split()
    for word in words:

    print "%s %s" % (word, 1)

    ioMapReduce.readInput(mapper)
    • Reducer
    #!/usr/bin/env python
    import sys

    import ioMapReduce


    def reducer(key,values):
    return len(values)


    ioMapReduce.outputWriter(reducer)
    Worthäufigkeit invertiert
    • bestimme zu jedem Wort seine Häufigkeit nach deren Häufigkeit sortiert
    • Aufruf
      $HD/bin/hadoop jar $HD/contrib/streaming/hadoop-0.20.1-streaming.jar -mapper $HD_W/mapperFrequency.py
      -reducer $HD_W/reducerFrequency.py -input $HD_W/grimmWordCount.out/part-00000 -output $HD_W/grimmFrequency.out
    • Mapper
    #!/usr/bin/env python

    import ioMapReduce

    def mapper(key,value):

    (word,freq) = value.split()
    print "%s %s" % (freq,word )

    ioMapReduce.readInput(mapper)
    • Reducer
    #!/usr/bin/env python
    import sys

    import ioMapReduce

    def reducer(key,values):

    return values

    ioMapReduce.outputWriter(reducer,lambda item: int(item[0]) )
    Pattern Matching
    • finde ein Wort in einer Datei und gib dessen Zeilennummer aus
    • Aufruf
      $HD/bin/hadoop jar $HD/contrib/streaming/hadoop-0.20.1-streaming.jar -mapper $HD_W/mapperFrequencyWord.py
      -reducer $HD_W/reducerFrequencyWord.py -input $HD_W/book/grimm.txt -output $HD_W/grimmFrequencyWord.out
    • Mapper
    #!/usr/bin/env python

    import ioMapReduce

    def mapper(key,value):

    words = value.split()
    for word in words:

    if ( word == "witch" ): print "%s %s" % (word,key)

    ioMapReduce.readInput(mapper)
    • Reducer
    #!/usr/bin/env python
    import sys

    import ioMapReduce

    def reducer(key,values):

    return sorted(values,key=int)

    ioMapReduce.outputWriter(reducer)

     

    Fazit

    • die Mapper und Reducer sind in Python einfach zu schreiben
    • das Aufsetzen des Frameworks stellt die grösste Hürde dar
    • right es fehlt jetzt nur noch 2000 Knoten
  •  

      Linux-Magazin
    TitelInhaltDatum
    Deko mit Nutzen Dekoratoren in Python 06/2009
    Im Zeichen der Drei Umstieg auf Python 3 09/2009
    Erfrischend neu C++0x, Teil 1: Die Erweiterung der Kernfunktionalität 04/2010
    Reichhaltiges Angebot C++0x, Teil 2: Die Erweiterung der Standardbibliothek 05/2010
    Magischer Mechanismus Template Metaprogramming mit C++ 01/2011
    Kurz und bündig Prägnante Programmierung in Haskell 06/2011
    Passendes Werkzeug C++ Compiler im Vergleich 12/2017
    Unterschiedlich Quicksort Implementierunge im Vergleich Quicksort 03/2021

     

      Linux-Magazin Online
    TitelInhaltDatum
    Funktionale Programmierung (1) Grundzüge der funktionalen Programmierung 09/2009
    Funktionale Programmierung (2) Funktionale Programmierung mit Python 10/2009
    Funktionale Programmierung (3) Das MapReduce Framework 11/2009

     

      Linux-Magazin Pro
    TitelInhaltDatum
    What’s new in Python 3 What do Python 2.0 programmers need to know about Python 3? 107/2009
    GCC, Clang, and MSVC compilers with C++ Perfect Match 207/2018
  • Rezensionen

      Linux-Magazin
    TitelAutorDatum
    Das Python Praxisbuch Farid Hajji 10/2009
    The C++ Standard Library Extension Pete Becker 02/2010
    Patterns For Parallel Software Design Jorge Luis Ortega-Arjona 07/2010
    Clojure Stefan Kamphausen, Tim Oliver Kaiser 01/2011
    The D Programming Language Andrei Alexandrescu 03/2011
    Haskell Intensivkurs Marco Block, Adrian Neumann 06/2011
    Learn You a Haskell for Great Good! Miran Lipovaca 06/2011
    Effektiv C++ programmieren Scott Meyers 10/2011
    Haskell: The Craft of Functional Programming Simon Thompson 02/2012
    Hadoop: Zuverlässige, verteilte und skalierbare Big-Data-Anwendungen Ramon Wartala 04/2012
    Multicore-Software Urs Gleim, Tobias Schüle 06/2012
    The C++ Standard Library Nicolai Josuttis 08/2012
    Template Metaprogramming Davide di Gennaro 11/2012
    Erlang/OTP Pavlo Baron 04/2013
    Scala in Depth Joshua Suereth 05/2013
    MapReduce Design Pattern Donald Miner, Adam Shook 06/2013
    Introducing Erlang Simon St. Laurent 06/2013
    The C++ Programming Language Bjarne Stroustrup 11/2013
    DSL Engineering Markus Völter 03/2014
    Requirements Engineering Ralf Baumann 08/2014
    Parallel und Concurrent Programming in Haskell Simon Marlow 09/2014
    From Mathematics to Generic Programming Alexander A. Stepanov 06/2015
    Discovering Modern C++ Peter Gottschling 05/2016
    Clean C++ Stephan Roth 03/2018
  •  

     
       
    TitelInhaltSourcecodeDatum
    Die Elf spielt auf Lambda-Funktionen I Listing 1
    Listing 2
    Listing 3
    12/2011
    Kurz und knackig Lambda-Funktionen II Listing 1
    Listing 2
    Listing 3
    Listing 4
    Listing 5
    02/2012
    Mehrgleisig unterwegs Multithreading I dotProduct.cpp
    dotProductPara.cpp
    04/2012
    Gemeinsam ins Ziel Multithreading II join.cpp
    forgetJoin.cpp
    bossWorker.cpp
    bossWorkerMutex.cpp
    bossWorkerLockGuard.cpp
    bossWorkerUniqueLock.cpp
    06/2012
    Im Gleichtakt Multithreading III boss2WorkerCondVar.cpp
    boss2WorkerLock.cpp
    bossWorker.cpp
    bossWorkerBlock.cpp
    08/2012
    Alle im Einklang Multithreading IV bossWorkerPrototype.cpp
    bossWorkerFutures.cpp
    10/2012
    Rasch verschoben Rvalue Referenzen bigArray.cpp
    bigArrayCopy.cpp
    bigArrayTime.cpp
    bigArrayCopyTime.cpp
    perfectForwarding.cpp
    12/2012
    Räumkommando unique_ptr autoPtr.cpp
    resourceGuard.cpp
    uniquePtr.cpp
    02/2013
    Klug aufgeräumt shared_ptr Listing 1
    Listing 2
    Listing 3
    Listing 4
    04/2013
    Zähl mich! Reguläre Ausdrücke I Listing 06/2013
    Starke Ausdrücke Reguläre Ausdrücke II allEmails.cpp
    match.cpp
    search.cpp
    08/2013
    Suchen und ersetzen Reguläre Ausdrücke III anonymizeIPs.cpp
    anonymizePartiallyIPs.cpp
    formateDate.cpp
    replaceDoubles.cpp
    10/2013
    Geschwindigkeit zählt Hashtabellen mapHash.cpp
    mapHashCompare.cpp
    12/2013
    Neue Ausdruckskraft auto, decltype und die Range-basierte For-Schleife auto.cpp
    out.cpp
    out3.cpp
    rangeBasedForLoop.cpp
    02/2014
    C++11 + 3 = C++14 C++14 automaticReturn.cpp
    lambda.cpp
    literal.cpp
    readerWriter.cpp
    04/2014
    Der Vertrag Das Memory-Modell syncAtomic.cpp
    syncAtomicAcquireRelease.cpp
    syncAtomicRelaxed.cpp
    syncMutex.cpp
    withoutSync.cpp
    06/2014
    Automatik mit Methode default, delete, override und final default.cpp
    defaultDelete.cpp
    final.cpp
    virtualFunctionsOverride.cpp
    08/2014
    Schönes Objekt

    Direkte Initialisierung

    Sequenzkonstruktor, Delegation und Vererbung von Konstruktoren

    sequenceConstructor.cpp
    directInitialization.cpp
    delegationConstructor.cpp
    inheritingConstructor.cpp
    10/2014
    Kurs zum Mars

    Raw-String- und benutzerdefinierte Literale

    Raw-String-Literale
    Benutzerdefinierte Literale

    12/2014
    Für vorsichtige Raser

    Type-Traits

    Listing 1
    Listing 2
    Listing 3
    Listing 4

    04/2015
    Konstante Magie

    constexpr

    Listing 1
    Listing 2
    Listing 3

    06/2015
    Punktlandung

    Variadic Templates...

    Listing 1
    Listing 2
    Listing 3
    Listing 4

    08/2015
    Containerverwaltung

    Neue Algorithmen für Container

    allAnyNoneOfMap.cpp
    allAnyNoneOfVec.cpp
    emplace.cpp
    shrinkToFit.cpp

    10/2015
    Der Reihe nach verpackt

    std::array, std::tuple und std::forward_list

    Listing 1
    Listing 2
    Listing 3
    Listing 4

    12/2015
    Doppelte Packung Referenz-Wrapper

    Listing 1
    Listing 2
    Listing 3

    02/2016
    Die Zeit verstehen Die Zeit Bibliothek I

    Listing 1
    Listing 2
    Listing 3
    Listing 4

    04/2016
    Pünktlich verschlafen Die Zeit Bibliothek II

    Listing 1

    06/2016
    Zukunftsmusik C++17 I

    Listing 1
    Listing 2
    Listing 3

    08/2016
    Bibliotheks-Karriere C++17 II

    Listing 1
    Listing 2
    Listing 3
    Listing 4
    Listing 5

    10/2016
    Von der Theorie zu Praxis Wie setzt der Programmierer die Feature von modernem C++ richtig ein?   12/2016
    Deliquente Typen Vermeide implizite Typkonvertierungen

    Listing 1
    Listing 2
    Listing 3
    Listing 4
    Listing 5
    Listing 6
    Listing 7
    Listing 8
    Listing 9

    02/2017
    Programmiere deklarativ  Programmiere deklarativ Listing 1
    Listing 2
    Listing 3
    Listing 4
    Listing 5
    04/2017
    Selbstoptimiert Unterstütze automatische Optimierung Listing 1
    Listing 2
    Listing 3
    06/2017
    Compiler first Sei nicht schlauer als der Compiler Listing 1
    Listing 2
    Listing 3
    08/2017
    Crash nach Flash Behalte das große Bild im Auge

    Listing 1
    Listing 2
    Listing 3
    Listing 4
    Listing 5
    Listing 6

    10/2017
    Kein Verlass Vermeide undefiniertes Verhalten Sourcecode 12/2017
    Schöne Lektüre Achte auf die Lesbarkeit des Codes Sourcecode 02/2018
    Von allen guten Geistern Lasse dir helfen Sourcecode 04/2018
    Schicke Bibliotheken Kenne deine Bibliotheken Sourcecode 06/2018
    Strebe nach Einfachheit Strebe nach Einfachheit Soucecode 08/2018
    Richtlinien-Kompetenz MISRA C++, Autosar C++14 und die C++ Core Guidelines   10/2018
    Goldene Erkenntniss Die C++ Core Guidelines Sourcecode 12/2018
    Gut verträglich Interfaces Sourcecode 02/2019
    Das gewisse Extra Die Guidelines Support Library (GSL) Sourcecode 04/2019
    In Portionen Funktionen Sourcecode 06/2019
    Fein abgeschmeckt Konstruktoren und Destruktoren Sourcecode 08/2019
    Klassisches Rezept Klassenhierachien Sourcecode 10/2019
    Falsche Zutaten Polymorphe Objekte Sourcecode 12/2019
    Auf Sparflamme Ressourcenverwaltung Sourcecode 02/2020
    Pointer-Rezepte Smart Pointer Sourcecode 04/2020
    Rezept für Namen Aussagekräftige Namen Sourcecode 06/2020
    Typische Fallen vermeiden Expressions und Statements Sourcecode 08/2020
    Richtig optimieren Performanz Sourcecode 10/2020
    Performance by Design Performanz Sourcecode 12/2020
    A Day at the Races Concurrency Sourcecode 02/2021
    Verletzungsrisiko Concurrency Sourcecode 04/2021
    Wenn es kracht Fehlerbehandlung Sourcecode 06/2021
    Unveränderlich Konstantheit und Unveränderlichkeit Sourcecode 08/2021
    Mit Schablone Templates Sourcecode 10/2021
    Rückschau 10 Jahre "Modernes C++ in der Praxis" Sourcecode 12/2021
    Schnittstellen Interfaces Sourcecode 02/2022
    Vergleichsweise Anpassung von Algorithmen Sourcecode 04/2022
    Container-Flitzer Sequenzielle Container Sourcecode 06/2022
    Nummernsucher Assoziative Container   08/2022
    Grenzfälle Zugriffe auf Elemente außerhalb eines Containers   01/2023
    The Famous Four Überblick zu Concepts, Ranges, Modulen und Coroutinen   04/2023
    Die verschiedenen Arten, Concepts zu verwenden Einsatz von Concpets   06/2023
    Die Definition von Concepts Benutzerdefinierte Concepts   08/2023
  • Öffentliche Vorträge

     
    JahrTitelVeranstaltungOrtDatumLängeMaterial
    2012 C++11: Quo vadis? CeBIT Hannover 10.03.2012 40 min video
      C++11: Quo vadis? Gesellschaft für Informatik Heidelberg 19.09.2012 90 min pdf
      C++11: Quo vadis? Gesellschaft für Informatik Karlsruhe 17.10.2012 90 min pdf
      C++11: Quo vadis? Gesellschaft für Informatik Dortmund 05.11.2012 90 min pdf
      C++11: An overview Meeting C++ Neuss 09.11.2012 90 min pdf video
      Functional Programming in C++11 Meetting C++ Neuss 10.11.2012 45 min pdf video
      C++11: Quo vadis? Gesellschaft für Informatik Ostwestfalen 04.12.2012 90 min pdf
    2013 Python, die Sprache für den Systemadministrator? CeBIT Hannover 05.03.2013 30 min  
      Embedded programming with C++11 Meeting C++ Düsseldorf 09.11.2013 60 min pdf
    2014 Embedded Programmierung in C++ Advanced Developers Konferenz Garching 29.04.2014 80 min pdf
      Funktionale Programmierung in C++ Advanced Developers Konferenz Garching 30.04.2014 80 min pdf
      Functional Programming in C++ C++ User Group Russia Saratov 24.10.2014 60 min pdf video
      Embedded Programmierung - die Domäne von C++? Embedded Software Engineering Kongress Sindelfingen 02.12.2014 40 min pdf
      Multithreading done right? Meeting C++ Berlin 02.12.2014 60 min pdf video
    2015 Multithreading done right? C++ Conference Moskau 26.02.2015 60 min pdfvideo
      Programmierung zur Compilezeit Advanced Developers Konferenz Erding 06.05.2015 80 min pdf
      Multithreading, richtig gemacht? Advanced Developers Konferenz Erding 06.05.2015 80 min pdf
      Funktionale Programmierung mit C++ Linuxtag Tübingen 13.06.2015 60 min pdf
      Functional Programming in C++ Central-European Functional Programming School Budapest 08.07.2015 90 min pdf
      Programmierung zur Compilezeit Embedded Software Engineering Kongress Sindelfingen 01.12.2015 40 min pdf
    2016 Das C++-Speichermodell Parallel 2016 Heidelberg 06.04.2016 75 min pdf
      15 Tipps (oder warum es nur 10 wurden) Advanced Developers Konferenz Erding 26.04.2016 80 min pdf
      Das C++-Speichermodell Advanced Developers Konferenz Erding 27.04.2016 80 min pdf
      Das C++-Speichermodell C++ Usergruppe München Planegg 28.04.2016 80 min pdf
      15 Tipps (oder warum es nur 10 wurden) Linuxtag Tübingen 11.06.2016 60 min pdf
      15 Tipps (oder warum es nur 10 wurden) oose Abendvortrag Hamburg 29.09.2016 70 min pdf
      The C++ memory model Meeting C++ Berlin 18.11.2016 60 min pdfvideo
      Funktionale Programmierung mit modernem C++ Embedded Software Engineering Kongress Sindelfingen 29.11.2016 45 min pdf
    2017 Funktionale Programmierung in C++ C++ Usergruppe Karlsruhe Karlsruhe 11.01.2017 60 min

    pdf audioTalk audioDiscussion

      Parallelism and Concurrency in C++17 and C++20 Multicore@Siemens Nürnberg 08.02.2017 45 min pdf
      Programming at Compile Time emBO++ Bochum 18.02.2017 45 min pdf
      Programming at Compile Time C++ Conference Moskau 24.02.2017 60 min pdf video
      Funktionale Programmierung in C++ sodge IT GmbH Balingen 13.03.2017 60 min pdf
      Gleichzeitigkeit und Parallelität in C++17 und C++20 Parallel 2017 Heidelberg 30.03.2017 70 min pdf
      Gleichzeitigkeit und Parallelität in C++17 und C++20 C++ Usergruppe München München 03.05.2017 70 min pdf
      Quo vadis Multithreading in C++ Advanced Developers Konferenz München 16.05.2017 70 min pdf
      C++17: Was gibts Neues? Advanced Developers Konferenz München 17.05.2017 70 min pdf
      Threads and Locks must go Meeting C++ Berlin 09.11.2017 60 min pdf video
      Secret Lightning Talk Meeting C++ Berlin 11.11.2017 10 min pdf video
      C++17 Embedded Software Engineering Kongress Sindelfingen 05.12.2017 45 min pdf
    2018 Best Practices für Concurrency Parallel 2018 Heidelberg 07.03.2018 70 min pdf
      Best Practices for Concurrency C++ Russia St. Petersburg 21.04.2018 60 min pdf
      Best Practices für Concurrency C++ Usergruppe Karlsruhe Karlsruhe 10.05.2018 70 min pdf
      Best Practices für Concurrency sodge IT GmbH Balingen 16.05.2018 70 min pdf
      Best Practices für Concurrency Linuxtag Tübingen 09.06.2018 60 min pdf
      Concurrency and Parallelism in C++17 and C++20/23 CoreHard Minsk 03.11.2018 50 min pdf
      The Core Guidelines for Safer Code Meeting Embedded 2018 Berlin 14.11.2018 30 min pdf
      Best Practices for Concurrency Meeting C++ Berlin 17.11.2018 60 min pdf
      Die C++ Core Guidelines für sicheren Code Embedded Software Engineering Kongress Sindelfingen 04.12.2018 40 min pdf
      Migration auf Python 3 Embedded Software Engineering Kongress Sindelfingen 04.12.2018 40 min pdf
    2019 Die bekanntesten (Online-)Compiler im Vergleich Parallel 2019 Heidelberg 21.02.2019 50 min pdf
      Concurreny und Parallelität mit C++17 und C++20/23 Parallel 2019 Heidelberg 21.02.2019 50 min pdf
      Concurrency and Parallelism in C++17 and C+20/23 Cpp Europe Bukarest 26.02.2019 60 min pdf
      Concurrency and Parallelism with C++17 and C++20/23 C++ Russia Moskau 20.04.2019 60 min pdf
      Concepts C++ Italia Mailand 15.06.2019 50 min pdfvideo
      C++20 - Die Revolution geht weiter Linuxtag Tübingen 06.07.2019 50 min pdf
      Concepts CppCon Aurora 16.09.2019 60 min pdfvideo
      Atomics, Locks, and Tasks (Back to Basics) CppCon Aurora 17.09.2019 2 * 60 min

    pdfexamples

    Video1 Video2

      C++20 - The Big Four C++ Russia St. Petersburg 01.11.2019 60 min pdf
      Concepts Meeting C++ Berlin 14.11.2019 60 min pdf video
      Die bekanntesten (Online-)Compiler im Vergleich Embedded Software Engineering Kongress Sindelfingen 03.12.2019 40 min pdf
     2020 Concepts C++ Usergruppe München  Online 26.03.2020 80 min  pdf
      Migration auf Python 3 enterPy Online 26.05.2020 45 min pdf
      Concepts Cpp Europe Online 23.06.2020 60 min pdf 
      Concepts C++ Usergruppe Karlsruhe/Dresden Online 10.07.2020 60 min pdf 
      From Functions to Coroutines CppCon Online 15.09.2020 60 min pdfvideo
      Smart Pointers (Back to Basics) CppCon Online 17.09.2020 60 min pdfvideo
      From Functions to Coroutines Meeting C++ Online 14.11.2020 60 min pdf
      C++20 - Die Revolution geht weiter Embedded Software Engineering Kongress Online 01.12.2020 40 min pdf
    2021 Erweitern und Einbetten von Python enterPy Online 15.04.2021 45 min pdf
      C++20 - Die Revolution geht weiter Advanced Developer Konferenz Online 18.05.2021 60 min pdf
      C++20 - Die Revolution geht weiter Uni Zwickau Online 17.06.2021 80 min pdf
      Concurrency Patterns CppCon Online 25.10.2021 60 min pdf video
      const and constexpr (Back to Basics) CppCon Online 26.10.2021 60 min pdf video
      Object Oriented Programming (Back to Basics) CppCon Online 27.10.2021 60 min pdf video
      C++20: The Small Pearls CppCon Online 28.10.2021 60 min pdf
      C++20: The Hidden Pearls Meeting C++ Online 11.11.2021 60 min pdf video
      Erweitern und Einbetten von Python Embedded Software Engineering Kongress Online 30.11.2021 40 min pdf
    2022 const and constexpr Meeting C++ Online 25.01.2022 60 min pdf video
      Extend and Embed Python Embo++ Online 26.03.2022 50 min pdf
      C++20: The Small Pearls ACCU Bristol 06.04.2022 90 min

    pdfvideo

      Ranges C++20 Techniques for Algorithmic Trading Online 26.04.2022 25 min pdf video (2:32)
      Concurrency Pattern Cpp Europe Online 24.05.2022 60 min pdf
      Extend and Embed Python C++ North Toronto 18.07.2022 60 min pdf video
    2023            
  •  

    Reactor Pattern

    Um was gehts

    Das Reaktor Pattern erlaubt es, einer ereignis-getriebenen Anwendung eine oder mehrere Client Anfragen gleichzeitig anzunehmen und auf verschiedene Serviceanbieter zu verteilen ( demultiplex and dispatch ) .

    Auch bekannt unter

    • Dispatcher
    • Notifier

    Beispiel

    • Ein Logging Server erhält mehrere Anfragen gleichzeitig und muß sie auf die verschieden Ausgabegeräte verteilen:
      NetworkLogging.gif
    • Eventhandling ins GUIs

    Anforderungen

    Ein Server soll mehrere Clientanfragen gleichzeitig beantworten können. Jede Clientanfrage besitzt eine eindeutige Indikation, die sie einem Serviceprovider zuordnen läßt. Folgende Bedingungen müssen für die Applikation gewährleistet sein:
    • sie soll nicht blockieren
    • sie soll auf maximalen Durchsatz ausgelegt sein und somit unnötiges Kontext wechseln, Daten synchronisieren oder Daten kopieren vermeiden
    • sie soll einfach um neue und verbesserte Service erweitert werden können
    • sie soll ohne komplexe multithreading und synchronisations Mechanismen auskommen

    Lösung

    Führe für jeden Servicetyp, den die Applikation anbietet, einen Handler ein. Dieser Eventhandler verarbeitet die spezifische Clientanfragen. Registriere den Handler beim Reaktor, der einem synchronen Event Verteiler (event demultiplexer) unterhält um auf eingehende Events zu reagieren. Wenn ein Event im synchronen event demultiplexerauftritt, benachrichtigt dieser den Reaktor, der das Event auf den angefragen Service verteilt.

    Struktur

    Handles

    • identifizieren Eventquellen wie Sockets, Filehandles oder Timersignale des OS-Systems
    • die Eventquellen erzeugen Events wie connect, read, time-out an, die auf den assoziierten Handle geschoben werden
    • der Handle kann nur die entsprechende Operation vollziehen

    synchrone event demultiplex

    • der Verteiler (demultiplexer) wartet auf Indikatoren (indication events), die auf einer Menge von Handles auftreten
    • bis die indication events abgeholt werden, blockiert der event _demultiplexer
    • auf dem assozierten Handle kann nun das eintreffende Ereignis aufgerufen

    Event Handler

    • definiert das Interface um indication events zu prozessieren
    • deklarieren die Services der Applikation

    Konkrete Event Handler

    • verarbeiten indication events in einer applikationsspezifischen Art
    • definieren die Services der Applikation

    Reaktor

    • stellt ein Interface zu Verfügung, damit die Event Handler inklusiver ihrer assozierten Handles registrieren und entfernen kann
    • der Reaktor benützt den synchronen Verteiler (event demultiplexer) um auf die Indikatoren (indicaton events) der Handles zu warten
    • beim Auftreten eines Indikators (indication events) ordnet der Reaktor dies Ereugnis dem entsprechenden Ereignis Handler zu
    • nach der Zuordnung des Ereignis an den entsprechenden Ereignis Handler, ruft ( dispatch ) der Reaktor die assozierte Methode auf dem Event Handler auf
    • der Reaktor startet und unterhält die event loop der Applikation
    Nicht die Applikation, sonder der Reaktor wartet auf indication events, die er auf die entsprechenden konkreten Event Handler verteilt ( demultiplex ) und dann deren assozierte Hook Methode aufruft ( dispatch ). Als Applikationsentwickler gilt es die spezifischen Event Handler zu implementieren und sie beim Reaktor zu registrieren.
    Der Reaktor als Framework stellt eine Ablaufumgebung für die Eventverarbeitung bereit. Diese inversion of control - die Applikation wird durch den Reaktor gesteurt - wird als Hollywood Prinzip bezeichnet.
    Don't call me, we call you.

    • Reaktor Klassendiagramm:
      reactorClassDiagram.gif

     

    Umsetzung

    Timer mit twisted

    Der Reaktor reagiert auf Zeittakte. Diese Ereignisse werden auf die registrierten Handler abgebildet. Sobald die Eventloop des Reaktors mittels reactor.run() gestartet wird, können die Ereignisse verarbeitet werden.
    import time

    from twisted.internet import task
    # http://twistedmatrix.com/trac/browser/trunk/twisted/internet/task.py
    from twisted.internet import reactor

    # http://twistedmatrix.com/trac/browser/trunk/twisted/internet/reactor.py

    # define handler as object
    class Handler():

    def __init__(self, Id ):

    self.__id= Id

    def __call__(self):

    print "Handler with id %s" % self.__id
    print "at %d:%d:%d%s\n" %(time.localtime()[3:6] + ( str(time.time() % 1)[1:] ,))


    # register handler as callable object
    l1 = task.LoopingCall(Handler(1))
    # start the task

    # start calls implicit reactor.callLater(... ) to reschedule the task ( fire the time event )
    l1.start(0.3) # call every 0.3 seconds

    l2 = task.LoopingCall(Handler(2))

    l2.start(1) # call every second

    # running the event loop
    reactor.run()

    Timer mit ACE

    • starte zwei Timer cb1 und cb2, die durch Signale SIGTSTP und SIGINT um den Faktor 10 abgebremst werden können
    Die ACE Variante ist deutlicher verboser, da hier auf Time- und Signalevents mit den entsprechenden CB und Signalhandler reagiert wird. Insbsondere sorgt der TimerDispatcher für das explizite Feueren der Timeevents.
    Durch die schedule Methode des Timers bzw. die register_handler des Reactors werden die Handler registriert. Während der CB Handler auf Timeevents mit handle_timeout reagiert, reagiert der Signalhandler auf Signalevents mit handle_signal. Beide Eventhandler werden durch die gleichen Verteiler bedient ( select ). Die ACE_Timer_Queue bzw. der konkrete Implementierung ACE_Timer_Heap merkt sich die die zukünftigen Zeitpunkte, zu denen sie expire an den Verteiler schickt.
    Die Methode wait_for_event startet die Eventloop, die dann auf die Timer- und Signalevents reagiert.
    #include "ace/Timer_Queue.h"
    #include "ace/Timer_Heap.h"
    #include "ace/Reactor.h"
    #include "CB.h"

    #include "SignalHandler.h"
    #include "TimerDispatcher.h"

    int main()
    {

    CB cb1, cb2;
    cb1.setID(1);
    cb2.setID(2);

    int arg1 = 1, arg2 = 2;

    ACE_Timer_Queue *timer_queue;

    ACE_NEW_RETURN(timer_queue, ACE_Timer_Heap, -1);

    // setup the timer queue

    Timer::instance()->set(timer_queue);

    ACE_Time_Value curr_time = ACE_OS::gettimeofday();

    ACE_Time_Value threeSeconds = curr_time + ACE_Time_Value(3L);
    ACE_Time_Value fourSeconds = curr_time + ACE_Time_Value(4L);

    // start in 3 seconds, each second
    long timerId1= Timer::instance()->schedule(&cb1, &arg1, threeSeconds, ACE_Time_Value(1));

    // start in 4 seconds; each 0.3 secondcs
    long timerId2=Timer::instance()->schedule(&cb2, &arg2, fourSeconds, ACE_Time_Value(0,300000));


    // Strg c
    SignalHandler *mutateTimer1= new SignalHandler( timerId1 );

    // Strg z
    SignalHandler *mutateTimer2= new SignalHandler( timerId2 );

    ACE_Reactor::instance()->register_handler( SIGINT, mutateTimer1);
    ACE_Reactor::instance()->register_handler( SIGTSTP, mutateTimer2);


    // "run" the timer.
    Timer::instance()->wait_for_event();

    return 0;

    }

     

    Dynamische Aspekte

    • Die Applikation registriert einen konkreten Eventhander beim Reaktor. Der Eventhandler drückt durch sein Implementierung aus, auf welche Art von Events er reagieren will. Typischerweise heißen die hook-Methoden handle_*, wie handle_input, handle_timeout, handle_put,... .
    • Durch eine get_handle des konkreten Eventhandlers Methode holt sich der Reaktor den spezifischen Handler.
    • Wenn alle Handles registriert sind, startet die Applikaton die Eventloop des Reaktors. Der Reaktor überwacht nun die Menge aller registrierten Handler auf das Eintreffen von indication Events .
    • Sobals ein Event auftritt, übergibt der synchrone event demultiplexer die Kontrolle an den Reaktor.
    • Der Reaktor benützt die Handles als Schlüssel, um die entsprechenden Eventhandler aufzurufen (demultiplex) und auf die hook Methode auszurufen (dispatch).
    • Die spezifische Methode des Eventhandler bearbeitet die Anfrage direkt auf dem Handle.

    Sichten des Programmierers

    • Reactor Pattern:
      reactor.jpg

    Anwendungsentwickler

    Ich will netzwerktransparent wissen, wie lange die Design Pattern Runde noch dauert? Oder ein bißchen formaler:
    Implementiere einen Server, der auf eine Browser Anfrage ( HTTP-GET ) ein html Seite schickt, die die verbleibende Zeit bis zum Ende der Design Pattern Runde darstellt. Noch formaler
    Client macht eine HTTP-GET Request right Server nimmt Request an und dispatcht sie auf den Event Handler right Event Handler schickt die Antwort zum Client Dazu müssen drei Schritte als Anwendungsentwickler und Nutzer der Reaktor Struktur implementiert werden.
    Ich verwende den BaseHTTPServervon Python.

    Request Handler implementieren

    class RequestHandler(BaseHTTPRequestHandler):


    def do_GET(self):
    import datetime
    actTime= datetime.datetime.now()

    endTime= datetime.datetime( 2007,5,22,9)

    diffSeconds= (endTime-actTime).seconds
    self.send_response(200)
    self.send_header('Content-type', 'text/html')

    self.end_headers()
    self.wfile.write("""<?xml version="1.0" ?>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>

    <title>Wie lange dauerts noch?</title>
    <script src="https://scwiki.science-computing.de/twiki/pub/TWiki/ChecklistPlugin/itemstatechange.js"
    language="javascript" type="text/javascript"></script></head>

    <body>

    <h1 align="center"><font size="10" color="#FF0000">Count down</font></h1>

    <p align="center"> <font size="6"> %s Sekunden noch bis zum Ende der Design Pattern Runde.</font> </p>

    </body>
    </html>""" %(str(diffSeconds)))

    Request Handler registrieren

    srvr = HTTPServer(("",4711),RequestHandler)

    Event loop laufen starten

    srvr.serve_forever()  

    Frameworkentwickler

    Ich will ein Reaktor Framework in Python entwickeln, das auf Input Events reagiert. Exemplarisch werden vier verschiedene Typen von Input Events gleichzeitig prozessiert und die entsprechenden Nachrichten in ähnlichnamige Dateien ins home geschrieben.
    1. stdin
      • jede stdin Eingabe erzeugt ein Event, das zur Prozessierung des Events führt
    2. lesen einer Datei
      • nach dem Einlesen der Datei wird der Handler wieder entfernt
    3. lesen einer url
      • nach dem Einlesen der Internetressource wird der Handler wieder entfernt
    4. HTTP - GET Anfrage
      • bei jedem stdin Event will ich wissen, wie lange die Design Pattern Runde noch dauert; dazu müssen periodisch folgende Schritte vollzogen werden
        • registriere den Event Handler, um den ReactorPattern Server zu fragen
        • darstellen des Ergebnisses auf stderr
        • deregistrieren des Event Handlers
    ##################
    #Event Handlers
    ##################

    import os
    import socket
    import sys
    import time


    class EventHandler:

    def handle_input(self,device):
    raise NotImplementedError

    def handle_output(self,device):
    raise NotImplementedError

    def handle_exception( self,device):
    raise NotImplmentedError

    def getHandle( self ):
    return NotImplementedError

    class InputEventHandler( EventHandler ):

    def __init__(self,device,dest):

    self.handleDevice= device
    self.outFile=open(dest,"a")

    firstLine= "Read from %s with handle %s \n" % ( device.name , device.fileno() )

    self.outFile.writelines([firstLine,"\n"])


    def handle_input(self,device):


    inp= device.readline().strip()
    self.outFile.write( inp + "\n" )

    self.outFile.flush()
    myReactor.registerHandler( InputToStderrEventHandler( urllib.urlopen("http://ackbar:4711"),

    os.getenv("HOME")+"/test.WieLangNoch" ) , "r")

    def getHandle( self ): return self.handleDevice

    class FileInputEventHandler( InputEventHandler ):

    def __init__(self,device,dest):

    self.handleDevice= device
    self.outFile=open(dest,"a")

    name=""
    try:
    name= device.name
    except:

    name= device.geturl()

    firstLine= "Read from %s with handle %s \n" % ( name , device.fileno() )

    self.outFile.writelines([firstLine,"\n"])


    def handle_input( self, device ):

    for line in device.readlines():
    self.outFile.write( line.strip() + "\n" )

    self.outFile.flush()
    Reactor().removeHandler( self ,"r" )

    class InputToStderrEventHandler( FileInputEventHandler ):

    def handle_input( self, device ):

    for line in device.readlines():
    self.outFile.write( line.strip() + "\n" )

    if line.startswith("<p align"):
    message= line.split(">")[2].split("<")[0]

    sys.stderr.write( message )
    self.outFile.flush()

    Reactor().removeHandler( self,"r")





    #############

    # Reactor
    #############

    class Singleton(object):
    def __new__(cls, *args, **kwds):

    it = cls.__dict__.get("__it__")
    if it is not None:

    return it
    cls.__it__ = it = object.__new__(cls)

    it.init(*args, **kwds)
    return it
    def init(self, *args, **kwds):

    pass



    import select

    class Reactor( Singleton ):

    readHandles={}
    writeHandles={}
    exceptHandles={}


    def registerHandler( self, eventHandler,eventTypes):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: Reactor.readHandles[handleId]= (handle,eventHandler)

    if "w" in eventTypes: Reactor.Reactor.writeHandles[handleId]= (handle,eventHandler)

    if "e" in eventTypes: Reactor.Reactor.exceptHandles[handleId]= (handle,eventHandler)


    def removeHandler( self, eventHandler ,eventTypes ):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: del Reactor.readHandles[handleId]

    if "w" in eventTypes: del Reactor.writeHandles[handleId]

    if "e" in eventTypes: del Reactor.exceptHandles[handleId]


    def handleEvents( self):

    while ( 1 ):

    rHandle, wHandle,eHandle= select.select( Reactor.readHandles.keys(), Reactor.writeHandles.keys(),
    Reactor.exceptHandles.keys() )

    print "all ready handle: Reactor.readHandles: %s Reactor.writeHandles: %s Reactor.exceptHandles: %s "
    %(rHandle,wHandle,eHandle)

    for han in rHandle:
    handleDevice= Reactor.readHandles[han][0]

    eventHandler= Reactor.readHandles[han][1]
    eventHandler.handle_input( handleDevice )

    for han in wHandle:
    handleDevice= Reactor.writeHandles[han][0]

    eventHandler= Reactor.writeHandles[han][1]
    eventHandler.handle_output( handleDevice )


    for han in eHandle:
    handleDevice= Reactor.exceptHandles[han][0]

    eventHandler= Reactor.exceptHandles[han][1]
    eventHandler.handle_exception( handleDevice )

    print "Reactor:handleEvents: waiting for input "

    import urllib

    myReactor=Reactor()
    myReactor.registerHandler( InputEventHandler( sys.stdin ,
    os.getenv("HOME")+"/test.stdin" ) ,"r")

    myReactor.registerHandler( FileInputEventHandler( open("/etc/services"),
    os.getenv("HOME")+"/test.services" ) ,"r")

    myReactor.registerHandler( FileInputEventHandler( urllib.urlopen("http://www.heise.de"),
    os.getenv("HOME")+"/test.heise" ) , "r")

    myReactor.handleEvents()

    Aspekte des Reaktor Frameworks

    Event Handler Interface festlegen
    class EventHandler:        

    def handle_input(self,device):
    raise NotImplementedError

    def handle_output(self,device):
    raise NotImplementedError

    def handle_exception( self,device):
    raise NotImplmentedError

    def getHandle( self ):
    return NotImplementedError
    Reaktor implementieren
    import select

    class Reactor( Singleton ):

    readHandles={}
    writeHandles={}
    exceptHandles={}


    def registerHandler( self, eventHandler,eventTypes):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: Reactor.readHandles[handleId]= (handle,eventHandler)

    if "w" in eventTypes: Reactor.Reactor.writeHandles[handleId]= (handle,eventHandler)

    if "e" in eventTypes: Reactor.Reactor.exceptHandles[handleId]= (handle,eventHandler)


    def removeHandler( self, eventHandler ,eventTypes ):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: del Reactor.readHandles[handleId]

    if "w" in eventTypes: del Reactor.writeHandles[handleId]

    if "e" in eventTypes: del Reactor.exceptHandles[handleId]


    def handleEvents( self):

    while ( 1 ):

    rHandle, wHandle,eHandle= select.select( Reactor.readHandles.keys(), Reactor.writeHandles.keys(),
    Reactor.exceptHandles.keys() )

    print "all ready handle: Reactor.readHandles: %s Reactor.writeHandles: %s Reactor.exceptHandles: %s "
    %(rHandle,wHandle,eHandle)

    for han in rHandle:
    handleDevice= Reactor.readHandles[han][0]

    eventHandler= Reactor.readHandles[han][1]
    eventHandler.handle_input( handleDevice )

    for han in wHandle:
    handleDevice= Reactor.writeHandles[han][0]

    eventHandler= Reactor.writeHandles[han][1]
    eventHandler.handle_output( handleDevice )


    for han in eHandle:
    handleDevice= Reactor.exceptHandles[han][0]

    eventHandler= Reactor.exceptHandles[han][1]
    eventHandler.handle_exception( handleDevice )

    print "Reactor:handleEvents: waiting for input "
    Um ein Eventhandler zu registrieren wird zusätzlich der Event Typ benötigt, für den sich der Event Handler interessiert.
    Durch die Registratur des Eventhandler ist es möglich, über den Filehandle ( z.B.: stdin = 0 ) sowohl das Fileobject, die Eventsource und den Eventhandler, die Anwendungslogik zu erhalten.
    In handle Events bedient sich der Reaktor dem nativen select Befehl um auf relevante Events registrieren zu können. handleEvents stellt die Eventloop des Reaktor dar, die, einmal gestartet, immer auf eingehende Events lauscht.

    Ausgabe, abhängig von den registrierten Event Handles

    • stdin wird registriert
      python reactorInput.py
      4
      all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
    Erst durch die Eingabe der Zahl 4 wird die Eventloop prozessiert. Ein Eingabe Event Reactor.readHandles: [0]liegt nun vor.
    • stdin, Datei und Url Request werden registriert
      python reactorInput.py
      all ready handle: Reactor.readHandles: [4, 6] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
      4
      all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
    Die Ressourcen file und url sind beim Starten der Eventloop registriert, daher werden sie sofort prozessiert all ready handle: Reactor.readHandles: [4, 6] Reactor.writeHandles: [] Reactor.exceptHandles: [] . Da ich sie explizit deregistriere sind sie bei in dem Eintreten eines stdin-Events nicht mehr vorhanden all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []. Der Reaktor prozessiert nun nur noch den Filedescriptor 0, also stdin.
    • stdin, Datei , Url und HTTP-GET Request werden registriert
      all ready handle: Reactor.readHandles: [4, 6] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
      4
      all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
      all ready handle: Reactor.readHandles: [4] Reactor.writeHandles: [] Reactor.exceptHandles: []
      49661 Sekunden noch bis zum Ende der Design Pattern Runde.Reactor:handleEvents: waiting for input
    Die stdin Abfrage registriet nun einen neuen Eventhandler, der die mir die Frage beantwortert: Wie lange dauert noch die Design Pattern Runde? Dieser Request erhält wieder den Filedescriptor 4. all ready handle: Reactor.readHandles: [4] Reactor.writeHandles: [] Reactor.exceptHandles: []

    Implementierung

    Die Implementierung des Reaktor Patterns lässt sich in zwei Schichten unterteilen. Die Frameworkschicht, die die applikationsunabhängige demultiplex/dispatch Infrastruktur zur Verfügung stellt und die Applikationschicht, die die konkreten Eventhandler liefert. In der klassischen, einfachsten Form, geschieht das ganz Eventhandling in einem Prozeß.

     Definiere das Event Handler Interfaces

    Die Methoden des Event Handlers legen das Servcie-Interface des Reaktor Frameworks fest.
    1. bestimme den Typ des dispatchingZiels
      • verwende eine Event Handler Objekt oder eine Event Handle Funktion
    2. bestimme die Event Handling dispatching Strategie
      1. dispatch auf eine einzelne Methode
        ...
        virtual void handlle_event( Handle handle, Event_Type et)= 0;
        ...
      2. dispatch auf mehrere Methoden
        ...
        virtual void handle_input( Handle handle )=0;
        virtual void handle_output( Handle handle )=0;
        virtual void handle_timeout( Handle handle )=0;
        ...
      • das eine Methode Interface erlaubt es einfach, das Framework um neue Eventtypen zu erweitern
      • während bei handle_event die ganze Verteilungsstrategie mittels Bedingungen auf Applikationsebene definiert werden muß, geschieht der dispatch auf dem reichhaltigeren Interface automatisch auf Frameworkebene
      • insbesondere ist bei feingranularen Dispatch möglich, spezielle hook Methoden in konkreten Event Handlern zu überschreiben

     Definiere das Reaktor Interface

    Die Applikation nutzt einerseits das Reaktor Interface um die spezifischen Event Handler zu de/registrieren und andererseits die Event Loop zu starten. Gerne wird das Reaktor Interface als Singleton implementiert, das die Anfragen an die Reaktor Implementierung delegiert. Neben dem Event Handler erhält erhält die register_handler als zweites Argument den Event Type als Argument, für den sie sich interessiert.
    void Select_Reactor_Implementation::register_handler( EventHandler* eventHandler, Event_Type event_type )

     Implementiere das Reaktor Interface

    • entkopple das Reaktor Interface von seiner Implementierung durch eine Brücke right mehrere verschiedene Implementierung können unterstützt werden ( select, poll, WaitFormMultipleObjects , GuiEventLoops ,... )
    • wähle einen synchronen event demutliplexer aus
    int select( u_int max_handle_plus_1 , 
    fd_set *read_fds, fd_set * write_fds, fd_set *except_fds,

    timeval *timeout);
    • implementiere ein demultiplexing table
      • ein Eintrag soll von der Form < handle, event_handle, indication event type > sein, wobei handle als Schlüssel für den Event Handler bei einem indication event ( connect, expire, read,... ) verwendet wird
    • definiere die Reaktor Implementierung
    # select Server( only demultiplexing ) 
    def get_request(self):

    while self._running:
    log.info('select on listen socket')
    # demultiplex

    rs, ws, es = select.select([self.socket], [], [], 10)

    if rs:
    log.info('accepting new connection')
    # socketobject and address
    return self.socket.accept()
    log.info('ending request loop')

    return (None, None)

     Bestimme die Anzahl der Reaktoren, die man benötigt

    • in der Regel sollte der Reaktor ein Singleton sein, jedoch erlaubt win32 nur 64 Handles pro Reaktor
    • aus Echtzeitforderungen kann es nötig sein mehrere Reaktoren gleichzeitig laufen zu lassen; Trennung der Reaktoren nach Eventtypen

     Implementiere die konkreten Eventhandler

    • sie stellen die Anwendungslogik dar, im Gegensatz zu dem bisher vorgestellten Reaktor-Framework
    • statte die Eventhandler gegebenfalls mit einem Zustand aus; vgl. Beispiel Timer mit ACE
    • implementiere die Eventhandler Funktionalität

    Kritik

    • Vorteile
      • klare Trennung von Framework- und Applikationslogik
      • Modularität von eventgetriebenen Anwendungen durch verschieden Eventhandler
      • Portabilität durch Trennung von Interface und Implementierung des Reaktors
      • einfache Parallelität durch den synchronen event demutliplexers
    • Nachteile
      • setzt einen event demultiplexer voraus
      • Durchsatzprobleme bei lang laufenden Event Handler in single Threaded Applikation, den der Event Handler blockiert den Reaktor
      • schwierig zu debuggen und zu testen durch die inversion of control

    Verwendete Patterns - Techniken

    • ObserverPattern
      • der Event Handler wird informiert, sobald ein für ihn spezifisches Event auftritt
    • BridgePattern
      • der Reaktor hält sich eine spezifische Reaktor Implementierung, an die er die Aufrufe delegiert
    • TemplateMethodePattern
      • die handle_* Methoden als hook Methoden werden wohl in statisch typisierten Programmiersprachen in einer definierten Reihenfolge prozessiert
    • double Dispatch: registerHandler right getHandle

     

  •  

    Threading in Clojure

    • http://de.wikipedia.org/wiki/Clojure
      Clojure ist ein moderner Lisp-Dialekt, der interaktive Entwicklung unterstützt.
      Die Sprache fördert einen funktionalen Stil, der nebenläufige Programmierung
      stark vereinfacht. Clojure läuft in der Java Virtual Machine und ist eng mit der
      Java Runtime integriert. Das Makrosystem ist mit dem anderer Lisp-Umgebungen vergleichbar.
        

    Die Ameisenanimation

    Die Ausgangsbedingung

    • Welt
      • besteht aus Zellen, die nur in Transaktionen verändert werden können
    • Zelle
      • enthält einen Betrag Nahrung und Duftstoff, eventuell eine Ameise und die zuhause Eigenschaft
    • Nahrung
      • ist über die Welt verteilt
    • Ameisen
      • sollen die Nahrung nach Hause bringen
      • zwei Ameisen können nicht gleizeitig in einer Zelle sein
      • Farbe der Ameise
        • schwarz: trägt keine Nahrung
        • rot: trägt Nahrung
        • besitzt eine Richtung und gegebenfalls Nahrung
      • jede Ameisen fällt autonom die Entscheidung, wohin sie laufen soll;
        die Auswahl wird zufällig gewählt, hängt aber von folgenden Fragen ab
        1. Wie weit ist Zuhause entfernt?
        2. Wie hoch ist der Duftstoffgrad der benachbarten Zellen?
        3. Hat sie schon Nahrung?
        4. Ist eine andere Ameise in einer benachbarten Zelle?
    • Zuhause
      • soll mit Nahrung aus der Welt gefüllt werden
    • Duftstoffe
      • werden von den Ameisen verteilt um die Pfade zu etablieren
      • verflüchtigen sich
    • Concurrency
      • jede Ameise, die Animation (Graphik) als auch der Duftstoff wird durch einen Thread simuliert
      • Aktionen geschehen nur in Transaktionen
      • die neue Berechnung der Animation und der Duftstoffwerte wird über Nachrichten (Agenten) angestossen
    • Graphik (Java Besonderheiten)
      • doto obj: wende die folgenden Methoden auf obj an
      • (proxy [class][] function-definitions... ): damit lässt sich eine Klasse class von Java in Clojure ableiten und implementieren

    Ameisen in Aktion

    ants.png

    Der Code

    • http://blip.tv/file/812787
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ant sim ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;   Copyright (c) Rich Hickey. All rights reserved.
      ;   The use and distribution terms for this software are covered by the
      ;   Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
      ;   which can be found in the file CPL.TXT at the root of this distribution.
      ;   By using this software in any fashion, you are agreeing to be bound by
      ;   the terms of this license.
      ;   You must not remove this notice, or any other, from this software.
      
      ;dimensions of square world
      (def dim 100)
      ;number of ants = nants-sqrt^2
      ;(def nants-sqrt 15)
      (def nants-sqrt 10)
      ;number of places with food
      (def food-places 80)
      ;range of amount of food at a place
      (def food-range 100)
      ;scale factor for pheromone drawing
      (def pher-scale 20.0)
      ;scale factor for food drawing
      (def food-scale 30.0)
      ;evaporation rate
      (def evap-rate 0.99)
      
      (def animation-sleep-ms 100)
      (def ant-sleep-ms 40)
      (def evap-sleep-ms 1000)
      
      (def running true)
      
      (defstruct cell :food :pher) ;may also have :ant and :home
      
      ;world is a 2d vector of refs to cells
      (def world
           (apply vector
                  (map (fn [_]
                         (apply vector (map (fn [_] (ref (struct cell 0 0)))
                                            (range dim))))
                       (range dim))))
      
      (defn place [[x y]]
        (-> world (nth x) (nth y)))
      
      (defstruct ant :dir) ;may also have :food
      
      (defn create-ant
        "create an ant at the location, returning an ant agent on the location"
      
        [loc dir]
          (sync nil
            (let [p (place loc)
                  a (struct ant dir)]
              (alter p assoc :ant a)
              (agent loc))))
      
      (def home-off (/ dim 4))
      (def home-range (range home-off (+ nants-sqrt home-off)))
      
      (defn setup
        "places initial food and ants, returns seq of ant agents"
        []
        (sync nil
          (dotimes [i food-places]
            (let [p (place [(rand-int dim) (rand-int dim)])]
              (alter p assoc :food (rand-int food-range))))
          (doall
           (for [x home-range y home-range]
             (do
               (alter (place [x y])
                      assoc :home true)
               (create-ant [x y] (rand-int 8)))))))
      
      (defn bound
        "returns n wrapped into range 0-b"
        [b n]
          (let [n (rem n b)]
            (if (neg? n)
              (+ n b)
              n)))
      
      (defn wrand
        "given a vector of slice sizes, returns the index of a slice given a
        random spin of a roulette wheel with compartments proportional to
        slices."
      
        [slices]
        (let [total (reduce + slices)
              r (rand total)]
          (loop [i 0 sum 0]
            (if (< r (+ (slices i) sum))
              i
              (recur (inc i) (+ (slices i) sum))))))
      
      ;dirs are 0-7, starting at north and going clockwise
      ;these are the deltas in order to move one step in given dir
      (def dir-delta {0 [0 -1]
                      1 [1 -1]
                      2 [1 0]
                      3 [1 1]
                      4 [0 1]
                      5 [-1 1]
                      6 [-1 0]
                      7 [-1 -1]})
      
      (defn delta-loc
        "returns the location one step in the given dir. Note the world is a torus"
        [[x y] dir]
          (let [[dx dy] (dir-delta (bound 8 dir))]
            [(bound dim (+ x dx)) (bound dim (+ y dy))]))
      
      ;(defmacro dosync [& body]
      ;  `(sync nil ~@body))
      
      ;ant agent functions
      ;an ant agent tracks the location of an ant, and controls the behavior of
      ;the ant at that location
      
      (defn turn
        "turns the ant at the location by the given amount"
      
        [loc amt]
          (dosync
           (let [p (place loc)
                 ant (:ant @p)]
             (alter p assoc :ant (assoc ant :dir (bound 8 (+ (:dir ant) amt))))))
          loc)
      
      (defn move
        "moves the ant in the direction it is heading. Must be called in a
        transaction that has verified the way is clear"
        [loc]
           (let [oldp (place loc)
                 ant (:ant @oldp)
                 newloc (delta-loc loc (:dir ant))
                 p (place newloc)]
               ;move the ant
             (alter p assoc :ant ant)
             (alter oldp dissoc :ant)
               ;leave pheromone trail
             (when-not (:home @oldp)
               (alter oldp assoc :pher (inc (:pher @oldp))))
             newloc))
      
      (defn take-food [loc]
        "Takes one food from current location. Must be called in a
        transaction that has verified there is food available"
        (let [p (place loc)
              ant (:ant @p)]
          (alter p assoc
                 :food (dec (:food @p))
                 :ant (assoc ant :food true))
          loc))
      
      (defn drop-food [loc]
        "Drops food at current location. Must be called in a
        transaction that has verified the ant has food"
      
        (let [p (place loc)
              ant (:ant @p)]
          (alter p assoc
                 :food (inc (:food @p))
                 :ant (dissoc ant :food))
          loc))
      
      (defn rank-by
        "returns a map of xs to their 1-based rank when sorted by keyfn"
        [keyfn xs]
        (let [sorted (sort-by (comp float keyfn) xs)]
          (reduce (fn [ret i] (assoc ret (nth sorted i) (inc i)))
                  {} (range (count sorted)))))
      
      (defn behave
        "the main function for the ant agent"
        [loc]
        (let [p (place loc)
              ant (:ant @p)
              ahead (place (delta-loc loc (:dir ant)))
              ahead-left (place (delta-loc loc (dec (:dir ant))))
              ahead-right (place (delta-loc loc (inc (:dir ant))))
              places [ahead ahead-left ahead-right]]
          (. Thread (sleep ant-sleep-ms))
          (dosync
           (when running
             (send-off *agent* #'behave))
           (if (:food ant)
             ;going home
             (cond
              (:home @p)
                (-> loc drop-food (turn 4))
              (and (:home @ahead) (not (:ant @ahead)))
                (move loc)
              :else
                (let [ranks (merge-with +
                              (rank-by (comp #(if (:home %) 1 0) deref) places)
                              (rank-by (comp :pher deref) places))]
                (([move #(turn % -1) #(turn % 1)]
                  (wrand [(if (:ant @ahead) 0 (ranks ahead))
                          (ranks ahead-left) (ranks ahead-right)]))
                 loc)))
             ;foraging
             (cond
              (and (pos? (:food @p)) (not (:home @p)))
                (-> loc take-food (turn 4))
              (and (pos? (:food @ahead)) (not (:home @ahead)) (not (:ant @ahead)))
                (move loc)
              :else
                (let [ranks (merge-with +
                                        (rank-by (comp :food deref) places)
                                        (rank-by (comp :pher deref) places))]
                (([move #(turn % -1) #(turn % 1)]
                  (wrand [(if (:ant @ahead) 0 (ranks ahead))
                          (ranks ahead-left) (ranks ahead-right)]))
                 loc)))))))
      
      (defn evaporate
        "causes all the pheromones to evaporate a bit"
      
        []
        (dorun
         (for [x (range dim) y (range dim)]
           (dosync
            (let [p (place [x y])]
              (alter p assoc :pher (* evap-rate (:pher @p))))))))
      
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; UI ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      (import
       '(java.awt Color Graphics Dimension)
       '(java.awt.image BufferedImage)
       '(javax.swing JPanel JFrame))
      
      ;pixels per world cell
      (def scale 5)
      
      (defn fill-cell [#^Graphics g x y c]
        (doto g
          (.setColor c)
          (.fillRect (* x scale) (* y scale) scale scale)))
      
      (defn render-ant [ant #^Graphics g x y]
        (let [black (. (new Color 0 0 0 255) (getRGB))
              gray (. (new Color 100 100 100 255) (getRGB))
              red (. (new Color 255 0 0 255) (getRGB))
              [hx hy tx ty] ({0 [2 0 2 4]
                              1 [4 0 0 4]
                              2 [4 2 0 2]
                              3 [4 4 0 0]
                              4 [2 4 2 0]
                              5 [0 4 4 0]
                              6 [0 2 4 2]
                              7 [0 0 4 4]}
                             (:dir ant))]
          (doto g
            (.setColor (if (:food ant)
                        (new Color 255 0 0 255)
                        (new Color 0 0 0 255)))
            (.drawLine (+ hx (* x scale)) (+ hy (* y scale))
                      (+ tx (* x scale)) (+ ty (* y scale))))))
      
      (defn render-place [g p x y]
        (when (pos? (:pher p))
          (fill-cell g x y (new Color 0 255 0
                                (int (min 255 (* 255 (/ (:pher p) pher-scale)))))))
        (when (pos? (:food p))
          (fill-cell g x y (new Color 255 0 0
                                (int (min 255 (* 255 (/ (:food p) food-scale)))))))
        (when (:ant p)
          (render-ant (:ant p) g x y)))
      
      (defn render [g]
        (let [v (dosync (apply vector (for [x (range dim) y (range dim)]
                                         @(place [x y]))))
              img (new BufferedImage (* scale dim) (* scale dim)
                       (. BufferedImage TYPE_INT_ARGB))
              bg (. img (getGraphics))]
          (doto bg
            (.setColor (. Color white))
            (.fillRect 0 0 (. img (getWidth)) (. img (getHeight))))
          (dorun
           (for [x (range dim) y (range dim)]
             (render-place bg (v (+ (* x dim) y)) x y)))
          (doto bg
            (.setColor (. Color blue))
            (.drawRect (* scale home-off) (* scale home-off)
                       (* scale nants-sqrt) (* scale nants-sqrt)))
          (. g (drawImage img 0 0 nil))
          (. bg (dispose))))
      
      (def panel (doto (proxy [JPanel] []
                              (paint [g] (render g)))
                   (.setPreferredSize (new Dimension
                                           (* scale dim)
                                           (* scale dim)))))
      
      (def frame (doto (new JFrame) (.add panel) .pack .show))
      
      (def animator (agent nil))
      
      (defn animation [x]
        (when running
          (send-off *agent* #'animation))
        (. panel (repaint))
        (. Thread (sleep animation-sleep-ms))
        nil)
      
      (def evaporator (agent nil))
      
      (defn evaporation [x]
        (when running
          (send-off *agent* #'evaporation))
        (evaporate)
        (. Thread (sleep evap-sleep-ms))
        nil)
      
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; use ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      (comment
      ;demo
      (load-file "ants.clj")
      (def ants (setup))
      (send-off animator animation)
      (dorun (map #(send-off % behave) ants))
      (send-off evaporator evaporation)
      
      )
        

    Charakteristiken

    Lisp Dialekt

    • Präfixnotation und Klammern
    • mächtiges Makrosystem right Clojure besitzt einen kleine Sprachkern, der Rest wird über Makros implementiert

    Funktionale Programmiersprache

    • Funktionen sind first-class functions (können Funktionen als Argument erhalten und zurückgeben)
    • Daten sind immutable (es gibt nur ausgewiesene veränderliche Daten (Referenztypen) für Concurrency mit einem expliziten Interface)
      • Rekurssion als Strukturelement (kein desctructive update (++i) möglich)
      • structural sharing für zusammengesetzte Datentypen right Veränderungen der Daten erzeugen nur eine neue Sicht auf die Daten
        clojure-trees.png

    Integration in die JVM

    • nutzt die Java Library right Clojure besitzt die mächtigste Library
    • erzeugt Bytecode
    • Java Interfaces können in Clojure implementiert werden
    • Java Code kann sehr kompakt geschrieben werden
      (import '(javax.swing JFrame JLabel JTextField JButton)
              '(java.awt.event ActionListener)
              '(java.awt GridLayout))
      (defn celsius []
        (let [frame (JFrame. "Celsius Converter")
              temp-text (JTextField.)
              celsius-label (JLabel. "Celsius")
              convert-button (JButton. "Convert")
              fahrenheit-label (JLabel. "Fahrenheit")]
          (.addActionListener convert-button
            (proxy [ActionListener] []
              (actionPerformed [evt]
                (let [c (Double/parseDouble (.getText temp-text))]
                  (.setText fahrenheit-label
                     (str (+ 32 (* 1.8 c)) " Fahrenheit"))))))
          (doto frame
            (.setLayout (GridLayout. 2 2 3 3))
            (.add temp-text)
            (.add celsius-label)
            (.add convert-button)
            (.add fahrenheit-label)
            (.setSize 300 80)
            (.setVisible true))))
      (celsius)
        
    • erzeugt: Celsius Convert:
      celsiusConvert.png

    Concurrency orientiert

    • setzt auf Javas Concurrency Fähigkeiten auf und bringt sie auf eine höhere Abstraktion
    • Daten sind immutable (Ausnahme Referenztypen und Java Typen)
    • Referenztypen (veränderliche Daten) sind über indirekte Referenzen implementiert right die Referenz auf den veränderten Wert wird atomar umgebogen

    Veränderliche Datentypen

    Frech nach Clojure - Grundlagen, Concurrent Programming, Java

    Die richtige Nutzung der Daten wird durch den Compiler sichergestellt. right Compilezeitfehler
    ReferenztypKontextKoordinationAusführung
    Var lokal eine Identität synchron
    Atom global eine Identität synchron
    Ref global mehrere Identitäten synchron
    Agent globals eine Identität asynchron

    • das Lesen und Schreiben der Referenztypen muß explizit über Funktionsaufrufe erfolgen
    • nur Vars lassen sich implizit referenzieren
    • die Referenzentypen sind als indirekt Referenzen implementiert
    • bis auf Vars können die veränderlichen Datentypen mit Validatoren versehen werden
      user=> (def myNum (atom 0 :validator integer?))
      #'user/myNum
      user=> (reset! myNum "Null")
      java.lang.IllegalStateException: Invalid reference state (NO_SOURCE_FILE:0)
      user=> (def myNewNum (atom "Null" :validator integer?))
      java.lang.RuntimeException: java.lang.IllegalStateException: Invalid reference state (NO_SOURCE_FILE:4)
        

    Thread lokale Daten - var

    Veränderungen an Varssind nur Thread lokal sichtbar.
    • Vars als globale Variablen besitzt ein Root-Binding
    • wird die globale Variable modifiziert, erhält der modifizierte Thread eine thread-lokale Referenz auf diese Variable

    Beispiel: Thread lokales Modifizieren von Daten

    • Code
      (def *wert* "Root-Binding")
      
      (defn thread1 []
        (println "Thread1-Root:" *wert*)
        (binding [*wert* "Lokal in Thread1"]
          (println "Thread1 binding vor sleep:" *wert*)
          (flush)
          (Thread/sleep 2000)
          (println "Thread1 binding nach sleep:" *wert*)))
      
      (defn thread2 []
        (Thread/sleep 1000)
        (println "Thread2-Root:" *wert*)
        (binding [*wert* "lokal in Thread2"]
          (println "Thread2 binding: " *wert*)))
      
      (do
        (.start (Thread. thread1))
        (.start (Thread. thread2)))
        
    • Ausgabe
      user=> (load-file "var.clj")
      Thread1-Root: Root-Binding
      Thread1 binding vor sleep: Lokal in Thread1
      nil
      user=> Thread2-Root: Root-Binding
      Thread2 binding:  lokal in Thread2
      Thread1 binding nach sleep: Lokal in Thread1
        

    Operationen

    OperationErläuterung
    def erzeugen einer Var

    Atomare Variablen - atom

    Atomekoordinieren Thread-übergreifend Lesen und Schreiben.
    • verwendet unter der Decke java.util.concurrent.atomic
    • im Konfliktfall wird die Veränderung des Atoms neu durchgeführt
    • die Updatefunktion sollte keine Nebeneffekte erzeugen

    Beispiel: Herunterzählen eines Countdowns

    • Code
      (def countdown (atom 1000))
      
      (def aufrufe (atom 0))
      
      (defn tick [akt-wert]
        (swap! aufrufe inc)
        (dec akt-wert))
      
      (do
       (dotimes [ _ 1000]
        (.start
          (Thread.
            #(swap! countdown tick)))))
        
    • Ausgabe
      user=> (load-file "atom.clj")
      nil
      user=> @countdown
      0
      user=> @aufrufe
      1014
        

    Operationen

    OperationErläuterung
    atom erzeugen eines Atoms
    deref, @ lesen des Wertes
    reset! setzen eines neuen Wertes
    swap! schreiben eines Wertes über eine Updatefunktion
    compare-and-set! bedingtes schreiben eines Wertes

    Software Transactional Memory

    Refserlauben das konsistente Verändern mehrerer Atome.
    • setzt Transaktionen aus dem Datenbankbereich auf den Speicherbereich umzusetzen (ACID)
      • A atomic: operationen auf den Daten erfolgen atomar
      • C consistent: die Daten in einer Transaktion sind immer in einem konsistenten Zustand
      • I isolated: Änderungen an den Daten sind ausserhalb der Transaktion nicht sichtbar, bevor die Transaktion nicht erfolgreich beendet wird
      • D durable: Daten sind nach der Transaktion auf einem Medium gesichert
    • ACI wird in Software Transaktional Memory umgesetzt
    • am Ende der Transaktion wird die Änderung veröffentlicht, falls keine anderen Transaktionen auf den Daten wirken (optimistischer Ansatz im Gegensatz zu Locks)
      • im Kollisionsfall muß die Transaktionen ihre Arbeit erneut aufnehmen (in Transaktionen dürfen keine Seiteneffekte geschehen)
    • falls eine andere Transaktion in Clojure einen Wert nur liest, führt dies nicht zum erneuten Aufruf einer Transaktion (Ausnahme ensure)
    • Transaktionen stellen eine hohe Anforderungen an Speicher und CPU gegenüber Locks, da jede Transaktion eine Kopie der Daten hält
    • Clojure lässt nur eine maximale Anzahl an Wiederholungen je Transaktion aus (1000)

    Beispiel: Lesend und Schreiben in einer Transaktion

    • Code
      (def ref1 (ref 1 ))
      
      (def ref2 ( ref 1 ))
      
      (defn lesethread []
        (println "Thread 1: lese ")
        (dotimes [i 6 ]
          (Thread/sleep 100)
          (dosync
            (println "ref1: " @ref1 "ref2: " @ref2))))
      
      (defn schreibthread[]
        (println "Thread2: schreibe")
        (dosync
          (alter ref1 inc)
          (Thread/sleep 300)
          (alter ref2 inc)))
      
      (do
        (.start (Thread. lesethread))
        (.start (Thread. schreibthread)))
      
        
    • Ausgabe
      user=> (load-file "ref.clj")
      Thread 1: lese
      nilThread2: schreibe
      
      user=> ref1:  1 ref2:  1
      ref1:  1 ref2:  1
      ref1:  1 ref2:  1
      ref1:  2 ref2:  2
      ref1:  2 ref2:  2
      ref1:  2 ref2:  2
         

    Operationen

    OperationErläuterung
    ref erzeugen einer Referenz
    dosync, io! erzeugen einer Transaktion
    deref, @ lesen einer Referenz
    ensure setzen eines neuen Wertes
    Wert kann in anderen Transaktionen nicht verändert werden
    ref-set setzen eines Wertes
    alter schreiben eines Wertes über eine Updatefunktion
    commute schreiben eines Wertes über eine Updatefunktion
    Werte in Transkationen dürfen sich überschreiben (last wins)

    Message Passing - agent

    Agentsarbeiten asynchron und seriell.
    • die Berechnung der Werte, die einem Agenten übermittel werden, geschieht in einem anderen Thread
    • die Updatefunkton wird als Aktion bezeichnet
    • Agentenaufrufe integrieren sich in das Transaktionssystem
    • während des Aktormodell in Erlang Nachrichten zwischen Prozessen übermittelt, arbeitet das Agentenmodell über Threads in einem Prozeß
    • über await oder await-for wird der aufrufende Thread blockiert

    Beispiel: Befüllen eines Vektors mit Strings

    • Code
      (def agts (agent ["James Bond"]))
      
      (defn gehaltsliste-Überwachen []
        (dotimes [i 10]
          (println i "Anzahl Agenten: " (count @agts ))
          (Thread/sleep 2)))
      
      (defn agenten-einstellen []
        (let [agenten ["Jerry Cotton"
      
                       "Phil Decker"
                       "John Steed"
                       "Emma Peel"
                       "Austing Powers"]]
          (doseq [a agenten]
            (send agts conj a)
            (println "Gesendet" a " -> " @agts)
            (Thread/sleep 2))))
      
      (do
        (.start (Thread. gehaltsliste-Überwachen))
        (.start (Thread. agenten-einstellen)))
      
        
    • Ausgabe
      user=> (load-file "agent.clj")
      0 Anzahl Agenten:  1
      1 Anzahl Agenten:  1
      nil
      user=> 2 Anzahl Agenten:  1
      Gesendet Jerry Cotton  ->  3 Anzahl Agenten:  2
      [James Bond Jerry Cotton]
      4 Anzahl Agenten:  2
      Gesendet Phil Decker  ->  [James Bond Jerry Cotton Phil Decker]
      5 Anzahl Agenten:  3
      6 Anzahl Agenten:  3
      Gesendet John Steed  ->  [James Bond Jerry Cotton Phil Decker John Steed]
      7 Anzahl Agenten:  4
      Gesendet Emma Peel  ->  [James Bond Jerry Cotton Phil Decker John Steed Emma Peel8 Anzahl Agenten:  5
      ]
      9 Anzahl Agenten:  5
      Gesendet Austing Powers  ->  [James Bond Jerry Cotton Phil Decker John Steed Emma Peel Austing Powers]
      
      user=>
        

    Operationen

    OperationErläuterung
    agent erzeugen eines Agenten
    deref, @ lesen einer Agenten
    send wendet die nicht blockierende update Funktion (Aktion)auf den Agent an
    send-off wendet die potenziell blockierende update Funktion (Aktion) auf den Agent an
    restart-agent setzt den Agent zurück
    await blockieren des aufrufenden Thread
    await-for blockieren des aufrufenden Thread für Millisekunden
    shutdown-agents schliesst den Threadpool aller Agenten

     

Mentoring

Stay Informed about my Mentoring

 

Rezensionen

Tutorial

Besucher

Heute 691

Gestern 3357

Woche 12254

Monat 39571

Insgesamt 3892285

Aktuell sind 32 Gäste und keine Mitglieder online

Kubik-Rubik Joomla! Extensions

Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode

Neuste Kommentare