2008-09-23 11 views
56

Ich bezweifle, dass es portabel gemacht werden kann, aber gibt es irgendwelche Lösungen da draußen? Ich denke, es könnte getan werden, indem man einen alternativen Stapel erstellt und SP, BP und IP bei der Funktionseingabe zurücksetzt, und die Ausbeute speichert IP und stellt SP + BP wieder her. Destruktoren und Ausnahmesicherheit erscheinen schwierig, aber lösbar.Wie implementieren Sie Coroutinen in C++

Wurde es gemacht? Es ist unmöglich?

+0

nur darauf hinweisen wollte, dass Koroutinen möglich ist in C++. Boost ist eine Möglichkeit. Die andere ist die als technische Spezifikation mit C++ 17 ratifizierte Koroutine. Es gibt bereits zwei Compiler mit Unterstützung (VC14 & Clang), und der TS wird es wahrscheinlich in die Sprache nach C++ 17 schaffen. Siehe meine Antwort für Details. – Atifm

+0

Für C-Programmierer, hier ist ein Artikel, Coroutines in C von Simon Tatham, der eine Reihe von Ansätzen hat. http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html einige sind komplexer als andere. –

Antwort

85

Ja es kann ohne ein Problem gemacht werden. Sie benötigen lediglich einen kleinen Assembler-Code, um den Aufruf-Stack in einen neu zugewiesenen Stack auf dem Heap zu verschieben.

Ich würde Blick auf die boost::coroutine Bibliothek.

Die eine Sache, auf die Sie achten sollten, ist ein Stapelüberlauf. Auf den meisten Betriebssystemen verursacht ein Überlauf des Stapels einen Segfault, da die Seite des virtuellen Speichers nicht zugeordnet ist. Wenn Sie jedoch den Stapel auf dem Heap zuweisen, erhalten Sie keine Garantie. Behalte das im Hinterkopf.

+101

Ich denke, es sollte ein Abzeichen geben, um die Arbeit "stackoverflow" in einem gültigen technischen Kontext auf SO! Erwähnen zu können! –

+7

Hier ist eine nette Standard-C++ - Lösung, die kein Boost erfordert: http://www.akira.ruc.dk/~keld/research/COROUTINE/ – Dan

+0

Wenn Sie den Stapel auf dem Heap zuordnen, können Sie das gleiche tun etwas wie den echten Stapel und setze eine Wächter-Seite am Ende (oder start, da es normalerweise rückwärts wächst), die auch einen segfault bei kleinen Überläufen verursacht. – Leushenko

5

Zeigt COROUTINE a portable C++ library for coroutine sequencing Sie in die richtige Richtung? Es scheint wie eine elegante Lösung, die den Test der Zeit bestanden hat ... es ist 9 Jahre alt!

Im DOC-Ordner befindet sich eine PDF-Datei mit dem Titel Eine portable C++ - Bibliothek für Coroutine-Sequenzierung von Keld Helsgaun, die die Bibliothek beschreibt und kurze Beispiele dafür bietet.

[Update] Ich mache es tatsächlich selbst erfolgreich. Neugier hat mich überwältigt, also habe ich nach dieser Lösung gesucht und festgestellt, dass sie gut zu einem Problem passt, an dem ich schon seit einiger Zeit arbeite!

7

Sie könnten besser mit einem Iterator als einer Coroutine arbeiten, wenn möglich. Auf diese Weise können Sie next() aufrufen, um den nächsten Wert abzurufen, aber Sie können Ihren Status als Mitgliedsvariablen anstelle von lokalen Variablen beibehalten.

Es könnte Dinge wartbarer machen. Ein anderer C++ - Entwickler kann die Coroutine möglicherweise nicht sofort verstehen, obwohl sie mit einem Iterator besser vertraut sind.

5

Ich glaube nicht, dass es viele vollständige, saubere Implementierungen in C++ gibt. Ein Versuch, den ich mag, ist protothread library.

0

Sie sollten stattdessen immer Threads verwenden; besonders in moderner Hardware. Wenn Sie Arbeit haben, die in Co-Routinen logisch getrennt werden kann, bedeutet die Verwendung von Threads, dass die Arbeit tatsächlich gleichzeitig ausgeführt wird, durch separate Ausführungseinheiten (Prozessorkerne).

Aber vielleicht möchten Sie Coroutinen verwenden, vielleicht weil Sie einen gut getesteten Algorithmus haben, der bereits auf diese Weise geschrieben und getestet wurde, oder weil Sie so geschriebenen Code portieren. Wenn Sie in Windows arbeiten, sollten Sie einen Blick auf fibers werfen. Fasern geben Ihnen einen koroutinenähnlichen Rahmen mit Unterstützung vom Betriebssystem.

Ich kenne andere Betriebssysteme nicht, um dort Alternativen zu empfehlen.

+7

Ich stimme nicht überein, blind Fäden über Fasern zu bevorzugen. In einem optimalen System ist die Anzahl der Threads, die versuchen zu laufen, gleich der Anzahl der Kerne (mehr, wenn Hyperthreading). In einer Lösung mit vielen Threads (100s) kann ein Threadpool, der Fasern ausführt, weitaus effizienter sein. –

+1

Danke für Ihre Kommentare. Ich sagte jedoch nicht "blind gefallen"; Ich sagte "immer in Betracht ziehen". Ich stimme zu, dass es Situationen gibt, in denen Fasern oder andere Coroutine-Methoden geeigneter sind, aber sie können sogar noch schwieriger sein, "richtig" zu werden als Fäden - und das sagt viel. Grundsätzlich schlage ich vor, dass Sie in den meisten Fällen standardmäßig Threads verwenden sollten, es sei denn, Sie können sich davon überzeugen, dass es gute Gründe gibt, sich für etwas anderes zu entscheiden. –

+5

Gewindeschneiden bedeutet Sperren, während Korotinen natürlich in Reihenfolge ausgeführt werden. Boom die Hälfte deiner Arbeit ist schon erledigt für dich. Threads sind gut, wenn Sie mehrere schwere Algorithmen parallel berechnen wollen, ich kann mir keinen anderen Grund vorstellen, sie zu benutzen. Ich denke, wenn es eine blockierende API gibt, die keinen nicht blockierenden Modus hat? –

17

In POSIX können Sie makecontext()/swapcontext() - Routinen verwenden, um Ausführungskontexte portabel zu wechseln. Unter Windows können Sie die Glasfaser-API verwenden. Ansonsten brauchen Sie nur ein bisschen Klebecode, der den Maschinenkontext wechselt. Ich habe Coroutinen sowohl mit ASM (für AMD64) als auch mit sapcontext() implementiert; keiner ist sehr schwer.

+14

Leider wurden 'makecontext()' und die damit verbundenen Funktionen im IEEE 1003.1 Posix Standard 2001 (http://pubs.opengroup.org/onlinepubs/009695399/functions/makekontext.html) als veraltet markiert und wurden aus diesem entfernt Standard in 2008 (http://blog.fpmurphy.com/2009/01/ieee-std-10031-2008.html). Auch bei älteren Pthread-Implementierungen sind diese Funktionen dafür bekannt, dass sie viele Dinge kaputt machen, und da sie jetzt nicht Standard sind, wird sich kaum jemand darum kümmern, sie wieder zu brechen. – LiKao

+1

Coroutines sind auf dem Weg zu einem Sprachfeature nach C++ 17: http://wg21.link/p0057r2 – Atifm

+1

Die Coroutinen in C++ 20 sind nicht die Corroutinen der OP wollen, weil sie stackless sind. – Lothar

0

WvCont ist ein Teil von WvStreams, der sogenannte Semi-Coroutinen implementiert. Diese sind ein wenig einfacher zu handhaben als vollständige Korutinen: Sie rufen sie an und geben sie an die Person zurück, die sie aufgerufen hat.

Es wird mit der flexibleren WvTask implementiert, die vollständige Coroutinen unterstützt; Sie können es in der gleichen Bibliothek finden.

Funktioniert auf Win32 und Linux, zumindest und wahrscheinlich jedem anderen Unix-System.

12

Für die Nachwelt

Dmitry Vyukov wondeful web site der hat einen cleveren Trick ucontext und setjump um Coutines in C++ zu simulieren.

Auch Oliver Kowalkes Kontext-Bibliothek war recently accepted in Boost, also werden wir hoffentlich eine aktualisierte Version von boost.coroutine sehen, die bald auf x86_64 funktioniert.

-2

habe ich versucht, mich Koroutinen mit C++ 11 und Threads zu implementieren:

,
#include <iostream> 
#include <thread> 

class InterruptedException : public std::exception { 
}; 

class AsyncThread { 
public: 
    AsyncThread() { 
     std::unique_lock<std::mutex> lock(mutex); 
     thread.reset(new std::thread(std::bind(&AsyncThread::run, this))); 
     conditionVar.wait(lock); // wait for the thread to start 
    } 
    ~AsyncThread() { 
     { 
      std::lock_guard<std::mutex> _(mutex); 
      quit = true; 
     } 
     conditionVar.notify_all(); 
     thread->join(); 
    } 
    void run() { 
     try { 
      yield(); 
      for (int i = 0; i < 7; ++i) { 
       std::cout << i << std::endl; 
       yield(); 
      } 
     } catch (InterruptedException& e) { 
      return; 
     } 
     std::lock_guard<std::mutex> lock(mutex); 
     quit = true; 
     conditionVar.notify_all(); 
    } 
    void yield() { 
     std::unique_lock<std::mutex> lock(mutex); 
     conditionVar.notify_all(); 
     conditionVar.wait(lock); 
     if (quit) { 
      throw InterruptedException(); 
     } 
    } 
    void step() { 
     std::unique_lock<std::mutex> lock(mutex); 
     if (!quit) { 
      conditionVar.notify_all(); 
      conditionVar.wait(lock); 
     } 
    } 
private: 
    std::unique_ptr<std::thread> thread; 
    std::condition_variable conditionVar; 
    std::mutex mutex; 
    bool quit = false; 
}; 

int main() { 
    AsyncThread asyncThread; 
    for (int i = 0; i < 3; ++i) { 
     std::cout << "main: " << i << std::endl; 
     asyncThread.step(); 
    } 
} 
+0

Ist das nicht nur eine Hersteller-Verbraucher-Implementierung? –

+2

Du hast mich verloren, als du Thread gesagt hast. Coroutinen sollten keine Threads benötigen. – Atifm

+2

Ja, das war vor langer Zeit, als ich nicht wirklich verstand, was Coroutines waren;) – jhasse

3

Eine neue Bibliothek Boost.Context wurde heute für die Umsetzung Koroutinen mit tragbaren Funktionen freigegeben.

9

Es gibt keine einfache Möglichkeit, Coroutine zu implementieren. Weil Coroutine selbst aus der Stapelabstraktion von C/C++ ist, genau wie Thread. Daher kann es nicht unterstützt werden, ohne Änderungen auf dem Sprachniveau zu unterstützen. Gegenwärtig (C++ 11) basieren alle existierenden C + + -Coroutine-Implementierungen alle auf Assembly-Level-Hacking, was schwierig ist, sicher und zuverlässig zu sein, wenn Plattformen überquert werden. Um zuverlässig zu sein, muss es Standard sein und von Compilern anstatt von Hacking gehandhabt werden.

Dafür gibt es eine standard proposal - N3708. Überprüfen Sie es, wenn Sie interessiert sind.

+2

Das Feature ist jetzt in einer technischen Spezifikation, geplant für post C++ 17: http://wg21.link/p0057r2 – Atifm

2

Ich bin mit einer Implementierung ohne asm Code gekommen. Die Idee ist, die Thread-Erstellungsfunktion des Systems zu verwenden, um Stapel und Kontext zu initialisieren, und setjmp/longjmp zu verwenden, um den Kontext zu wechseln. Aber es ist nicht tragbar, siehe tricky pthread version, wenn Sie interessiert sind.

3

Dies ist ein alter Thread, aber ich möchte ein Hack Duff Vorrichtung vorzuschlagen, die nicht os abhängig (soweit ich mich erinnere) ist: hier

C coroutines using Duff's device

Und als Beispiel ist eine Telnet-Bibliothek, die ich geändert habe, um Coroutinen anstelle von fork/threads zu verwenden:

Und da Standard C vor C99 im Wesentlichen eine echte Teilmenge von C++ ist, funktioniert das auch gut in C++.

4

Für diejenigen, die wissen möchten, wie sie Coroutinen in C++ portabel nutzen können, müssen Sie auf C++ 17 warten. Das Normungsgremium arbeitet an dem Feature, siehe N3722 paper. Um den aktuellen Entwurf des Papiers zusammenzufassen, statt Async und Await, werden die Keywords fortsetzbar sein und warten.

Werfen Sie einen Blick auf die experimentelle Implementierung in Visual Studio 2015, um mit Microsofts experimenteller Implementierung zu spielen. Es sieht nicht so aus als ob clang eine Implementierung hat.

Es gibt ein gutes Gespräch von Cppcon Coroutines a negative overhead abstraction skizzieren die Vorteile der Verwendung von Coroutines in C++ und wie es die Einfachheit und Leistung des Codes beeinflusst.

Gegenwärtig müssen wir noch Bibliotheksimplementierungen verwenden, aber in naher Zukunft werden wir Coroutinen als Kern-C++ - Funktion haben.

Update: Sieht so aus, als ob die Coroutinimplementierung es nicht in C++ 17 schafft, aber es wird eine technische Spezifikation sein (p0057r2). Auf der Oberseite sieht es so aus, als ob sie Unterstützung beim Claming mit dem Flag -fcoroutines_ts und in Visual Studio 2015 Update 2 ist. Den Keywords wird auch ein co_ vorangestellt. Also co_await, co_yield usw.

1

https://github.com/tonbit/coroutine ist C++ 11 einzelne .h asymmetrische Coroutine-Implementierung unterstützt Lebenslauf/Ausbeute/erwarten Primitiven und Channel-Modell. Es implementiert über ucontext/fiber, nicht abhängig von boost, läuft unter linux/windows/macOS. Es ist ein guter Ausgangspunkt, um die Implementierung von Coroutine in C++ zu erlernen.

Verwandte Themen