2012-04-01 8 views
6

Ich habe einen benutzerdefinierten streambuf für gepufferte Netzwerk-Socket-I/O, Override-Unterlauf, Überlauf und Sync abgeleitet, so dass Unterlauf ist Thread-Safe mit dem Satz der anderen beiden, (Ich habe separaten Eingang und Ausgang interne Puffer) . Das funktioniert gut, aber ich möchte dies für Vollduplex-I/O verwenden, wo ein Thread eingeben kann, während ein anderer ausgibt, also möchte ich einen istream für den empfangenden Thread und ostream für den sendenden verwenden, während ich das Netzwerk teile streambuf als das alles Socket Zeug abstrahiert. Meine Frage ist, inwiefern sind die streambuf-Mitglieder von Eingabeoperationen auf einem istream betroffen, die von den streambuf-Mitgliedern getrennt sind, die von Ausgabeoperationen in einem ostream betroffen sind, wenn die Eingabe- und Ausgabepuffer getrennt sind?istream und ostream mit gemeinsamem streambuf, beide Threads sicher für Duplex I/O?

Es wäre besser, in der Lage zu sein, dies zu tun, anstatt die Socket-Sachen von meiner Streambuf-Abstraktion trennen zu müssen, damit der Socket zwischen istream und ostream mit separaten streambufs geteilt werden kann - dann würde ich auch zwei Versionen von der streambuf - einer mit einem einzigen internen Puffer (nur zur Verwendung in entweder istream oder ostream) und einer mit zwei internen Puffern, wie ich sie jetzt für die Verwendung in iostream ... habe, saugt als zusätzliche Klassen und Code-Duplizierung.

+0

Auf den ersten Blick hatte ich einige Zweifel, aber das sieht nach der besten Frage aus, die ich seit einiger Zeit gesehen habe. +1 – Potatoswatter

Antwort

2

Es gibt keine spezielle Garantie für std::streambuf (oder std::basic_streambuf<...>), die mehr Garantien gibt, als allgemein angegeben. Das heißt, Sie können den Status des Objekts jederzeit durch mehrere Threads lesen, aber wenn ein Thread den Status des Objekts ändert, darf kein anderer Thread auf das Objekt zugreifen. Sowohl lesende als auch schreibende Zeichen modifizieren den Zustand des Strompuffers, d.h. von einem formalen Standpunkt aus können sie nicht ohne externe Synchronisation verwendet werden.

Intern sind die beiden Puffer vollständig getrennt und haben nichts miteinander zu tun. Die Operationen in Stream-Puffern modifizieren sie auf eine ziemlich strukturierte Art und ich kann mir nicht vorstellen, dass irgendeine Implementierung eine explizite Interaktion zwischen den beiden Gruppen von Zeigern hätte. Das heißt, in der Praxis glaube ich nicht, dass zwischen Lesen und Schreiben eine Synchronisation notwendig ist. Ich hatte jedoch zuvor nicht erkannt, dass die zwei Sätze von Pufferzeigern tatsächlich die gleichen Cache-Zeilen teilen können, die zumindest Leistungsprobleme verursachen können. Ich denke nicht, dass dies irgendwelche Korrektheitsprobleme verursachen sollte.

Die einzige möglicherweise zwischen den beiden Stream-Puffern freigegebene Ressource ist das Objekt std::locale, das jedoch zustandslos sein soll. Außerdem verwendet std::streambuf dieses Objekt nicht selbst: Es ist Ihr Stream-Puffer, der einige Facetten verwenden kann (z. B. die Facette std::codecvt<...>). Wenn das Gebietsschema über einen Aufruf der virtuellen Funktion imbue() geändert wird, können Sie diese Änderung abfangen und die erforderliche Synchronisierung ausführen, wenn der Stream-Puffer das Gebietsschema verwendet.

Zusammenfassend gibt der Standard keine Garantie, dass es funktioniert, gleichzeitige Threads zum Lesen und Schreiben mit dem gleichen Stream-Puffer zu verwenden.In der Praxis ist das DS9k wahrscheinlich das einzige System, bei dem es fehlschlagen würde, und die beiden Threads können aufgrund der Pufferzeiger, die in gemeinsam genutzten Cache-Zeilen enden, effektiv synchronisiert werden.

+0

" ... das std :: locale-Objekt, das zustandslos sein soll ... " Bemerkenswert ist, dass er zwei "mbstate_t" Mitglieder braucht, die für Input und Output getrennt sind. Was ist DS9k? – Potatoswatter

+0

Die [Death Station 9000 (DS9k)] (http://en.wikipedia.org/wiki/User:CompuHacker/CHDS9000) ist eine hypothetische und bewusst so wenig hilfreich wie möglich Implementierung des Standards: wo jede Freiheit gegeben ist, verwendet es dies, um so viel Schaden wie möglich zu verursachen. –

+0

Ich sehe, dass istream eine sync() -Member hat, die das streambuf sync aufrufen wird (Ich habe nichts gesehen andere Mitglieder von Istream nennen sync() selbst, also nehme ich an, dass es nur ein Problem ist, wenn der Benutzer es aufruft, und ich kann es überschreiben, um entweder nichts zu tun oder den streambuf zu versuchen, mehr Daten vom Socket in den Puffer zu lesen . –

2

Die Ein- und Ausgabesequenzen sind im Wesentlichen unabhängig. Es gibt ein schönes Diagramm bei cppreference.com:

Diagram of streambuf members

Das einzige, was zwischen den Eingangs- und Ausgang Sequenzen gemeinsam ist das locale Objekt, das die codecvt Facette enthält verwendet, um Textcodierung Übersetzung durchzuführen.

Theoretisch wäre das Ändern der Textcodierung für Midstream Thread-unsicher, aber in der Praxis unterstützen die Bibliotheken diese Operation überhaupt nicht!

Sie sollten gut gehen.

+0

Das Ändern des lokalen Midstream würde zu inkonsistentem Verhalten führen (zumindest für den Codecvt kann es in der mittleren Übersetzung (do_in/do_out) eines Chunks von Input/Output sein). Daher würde das Ändern eines Locals nach dem Lesen/Schreiben eines Streams aus einem Stream als eine schlechte Idee erscheinen. Eine Folge davon ist, dass die Klasse stream_buf abgeleiteten Klassen ermöglicht, die Facettenwerte, die von einem Gebietsschema abgerufen werden, zwischenzuspeichern. Auch einige Streams setzen Einschränkungen/Bedingungen für das Ändern des lokalen, wenn sie nicht am Anfang sind (dh nachdem das Lesen/Schreiben durchgeführt wurde). –

+0

@Loki: Das Ändern der Gebietsschema Midstream ist wie das Schließen der Datei und das erneute Öffnen mit einem anderen Gebietsschema. Es gibt keine konzeptionellen Barrieren; Das Schreiben einer ungültigen Sequenz führt zu einer Ausnahme. Es wird vom Standard unterstützt, aber von wenigen oder gar keinen Implementierungen. – Potatoswatter

+0

Ich sehe keinen Verweis auf das Öffnen und Schließen der Streams bei Verwendung von imbue() oder pubimbue() im Standard. Wie oben ausgeführt, gibt es konzeptionelle Barrieren. –

Verwandte Themen