Ereignis- / Aufgabenwarteschlange Multithreading C ++

8

Ich möchte eine Klasse erstellen, deren Methoden aus mehreren Threads aufgerufen werden können. aber anstatt die Methode im Thread auszuführen, von dem sie aufgerufen wurde, sollte sie alle in ihrem eigenen Thread ausführen. Kein Ergebnis muss zurückgegeben werden und es sollte den aufrufenden Thread nicht blockieren.

Ein erster Versuch Implementierung habe ich unten enthalten. Die öffentlichen Methoden fügen einen Funktionszeiger und Daten in eine Job-Warteschlange ein, die der Worker-Thread dann aufnimmt. Es ist jedoch nicht besonders schön, Code und das Hinzufügen neuer Methoden ist umständlich.

Idealerweise möchte ich dies als eine Basisklasse verwenden, die einfach Methoden (mit einer variablen Anzahl von Argumenten) mit minimaler Hastle und Code-Duplizierung hinzufügen kann.

Was ist ein besserer Weg, dies zu tun? Gibt es einen vorhandenen Code, der etwas Ähnliches tut? Danke

%Vor%     
Ben Reeves 29.05.2009, 00:43
quelle

8 Antworten

6

Es gibt Futures Bibliothek, die ihren Weg in Boost und die C ++ - Standardbibliothek. Es gibt auch etwas Ähnliches in ACE, aber ich würde es jedem nur wärmstens empfehlen (wie bereits von @lothar gesagt, es ist Active Object).

    
Nikolai Fetissov 29.05.2009, 01:05
quelle
2

Die POCO -Bibliothek hat etwas in der gleichen Richtung namens ActiveMethod (zusammen mit einigen verwandten Funktionen wie ActiveResult) im Threading-Bereich. Der Quellcode ist leicht verfügbar und leicht zu verstehen.

    
Duck 29.05.2009 01:20
quelle
2

Sie können dies lösen, indem Sie Boost's Thread-library verwenden. So etwas (halb-pseudo):

%Vor%

Beachten Sie auch, wie RAII uns ermöglicht, diesen Code kleiner und besser zu verwalten.

    
nhaa123 29.05.2009 08:42
quelle
1

Sie könnten an Active Object eines der ACE-Muster von ACE-Framework .

Als Nikolai wies darauf hin Futures sind geplant für Standard C ++ einige Zeit in die Zukunft (Wortspiel beabsichtigt).

    
lothar 29.05.2009 00:57
quelle
1

Für Erweiterbarkeit und Wartbarkeit (und andere Fähigkeiten) könnten Sie eine abstrakte Klasse (oder Schnittstelle) für den "Job" definieren, den der Thread ausführen soll. Dann würden die Benutzer Ihres Threadpools diese Schnittstelle implementieren und dem Threadpool einen Verweis auf das Objekt geben. Dies ist dem Symbian Active Object Design sehr ähnlich: Jede AO-Subklasse von CActive muss Methoden wie Run () und Cancel () implementieren.

Der Einfachheit halber könnte Ihre Schnittstelle (abstrakte Klasse) so einfach sein wie:

%Vor%

Dann hätte der Thread-Pool oder die Anforderungen für die Annahme einzelner Threads etwa Folgendes:

%Vor%

Natürlich würden Sie mehrere Aufgaben haben, die alle Arten von zusätzlichen Setter / Getter / Attribute und was auch immer Sie in jedem Lebensabschnitt benötigen. Das einzige Muss ist jedoch, die Methode Run () zu implementieren, die die langwierigen Berechnungen durchführt:

%Vor%     
Ignas Limanauskas 29.05.2009 01:26
quelle
1

Hier ist eine Klasse, die ich für einen ähnlichen Zweck geschrieben habe (ich benutze sie für die Ereignisbehandlung, aber Sie könnten sie natürlich in ActionQueue umbenennen und ihre Methoden umbenennen).

Du benutzt es so:

Mit der Funktion, die Sie aufrufen möchten: void foo (const int x, const int y) { /*...*/ }

Und: EventQueue q;

q.AddEvent (boost :: bind (foo, 10, 20));

Im Worker-Thread

q.PlayOutEvents ();

Hinweis: Es sollte ziemlich einfach sein, unter Bedingungen Code hinzuzufügen, um CPU-Zyklen zu vermeiden.

Der Code (Visual Studio 2003 mit Boost 1.34.1):

%Vor%

Ich hoffe, das hilft. :)

Martin Bilski

    
Martin Bilski 29.05.2009 11:33
quelle
1

Unten ist eine Implementierung, die keine "functionProxy" -Methode benötigt. Obwohl es einfacher ist, neue Methoden hinzuzufügen, ist es immer noch unordentlich.

Boost :: Bind und "Futures" scheinen so zu sein, als würden sie viel aufräumen. Ich denke, ich werde mir den Boost-Code ansehen und sehen, wie es funktioniert. Danke für deine Vorschläge alle.

GThreadObject.h

%Vor%

GThreadObject.cpp

%Vor%

main.cpp

%Vor%

Edit: Nur der Vollständigkeit halber habe ich eine Implementierung mit Boost :: bind gemacht. Hauptunterschiede:

%Vor%

Die Boost-Implementierung für 10.000.000 Iterationen von functionOne () dauerte ~ 19sec. Die Nicht-Boost-Implementierung dauerte jedoch nur ~ 6,5 Sekunden. Also ca. 3x langsamer. Ich denke, eine gute nicht-schließende Warteschlange zu finden, wird hier der größte Performance-Flaschenhals sein. Aber es ist immer noch ein großer Unterschied.

    
Ben Reeves 29.05.2009 14:10
quelle
0

Sie sollten sich die Boost ASIO-Bibliothek ansehen. Es wurde entwickelt, um Ereignisse asynchron zu versenden. Es kann mit der Boost-Thread-Bibliothek kombiniert werden, um das von Ihnen beschriebene System zu erstellen.

Sie müssten ein einzelnes Objekt boost::asio::io_service instanziieren und eine Reihe asynchroner Ereignisse planen ( boost::asio::io_service::post oder boost::asio::io_service::dispatch ). Als Nächstes rufen Sie die run -Memberfunktion aus n Threads auf. Das Objekt io_service ist Thread-sicher und garantiert, dass Ihre asynchronen Handler nur in einem Thread ausgeführt werden, von dem Sie io_service::run aufgerufen haben.

Das Objekt boost::asio::strand eignet sich auch für die einfache Thread-Synchronisation.

Für was es wert ist, denke ich, dass die ASIO-Bibliothek eine sehr elegante Lösung für dieses Problem ist.

    
Dan 29.05.2009 12:22
quelle

Tags und Links