Kann ein Programm gleichzeitig fflush() für dieselbe FILE* aufrufen?

Lesezeit: 7 Minuten

Benutzeravatar von Serge Rogatch
Serge Rogatch

Kann etwas Schlimmes passieren (wie undefiniertes Verhalten, Dateibeschädigung usw.), wenn mehrere Threads gleichzeitig aufrufen fflush() auf demselben FILE* Variable?

Klarstellung: Ich meine nicht, die Datei gleichzeitig zu schreiben. Ich meine nur gleichzeitig spülen.

Die Threads lesen oder schreiben die Datei nicht gleichzeitig (sie schreiben die Datei nur in einen kritischen Abschnitt, einen Thread nach dem anderen). Sie spülen nur außerhalb des kritischen Abschnitts, um den kritischen Abschnitt früher freizugeben, damit die anderen die andere Arbeit erledigen können (außer dem Schreiben von Dateien).

Es kann jedoch vorkommen, dass ein Thread die Datei schreibt (innerhalb des kritischen Abschnitts), während ein anderer Thread die Datei leert (außerhalb des kritischen Abschnitts).

  • Das hört sich so an, als würden Sie es aus einer Design-Perspektive fragen, anstatt zu versuchen, eine Erklärung für einen Fehler zu beseitigen – vielleicht wäre es sinnvoll, dies expliziter zu machen.

    – PJTrail

    24. August 2016 um 10:54 Uhr

  • Angesichts der Antwort, dass FILEs Thread-sicher sind, könnte eine negative Folge sein, dass ein Thread gezwungen ist, auf das Leeren eines anderen Threads zu warten, obwohl dies wahrscheinlich nur dann von Bedeutung wäre, wenn Sie eine ziemlich hohe Leistung benötigen. Mit Funktionen wie flockfile oder flock? könnte denkbar zum Deadlock führen, aber wahrscheinlich nicht in dem von Ihnen beschriebenen Fall.

    – PJTrail

    24. August 2016 um 11:07 Uhr


  • @PJTraill , das ist in Ordnung: Wenn ein Thread spült und ein anderer Thread etwas schreibt und dann herausfindet, dass er auch spülen muss, kann er nur darauf warten, dass der erste Thread zurückgibt, worum es geht FILE* in einen konsistenten Zustand bringen (es wird alles gelöscht, was er geschrieben hat, und vielleicht sogar, was der zweite Thread geschrieben hat – dies hängt von der Rasse ab), und dann sollte der zweite Thread sicherstellen, dass das, was er gerade geschrieben hat, auch gelöscht wird. Natürlich gibt es einen besseren Ansatz mit asynchronem Leeren in einem dedizierten Thread, aber das ist komplexer und daher außerhalb des Bereichs.

    – Serge Rogatch

    27. August 2016 um 16:48 Uhr

  • Sie scheinen davon auszugehen, dass das Spülen nur andere Threads verzögert, die ebenfalls versuchen, zu spülen, aber ich verstehe die Anführungszeichen in der Antwort von 2501 so, da es a Single pro Stream zu sperren, verzögert es auch Threads, die versuchen zu schreiben – und das könnte unerwünscht sein!

    – PJTrail

    31. August 2016 um 14:11 Uhr

Benutzeravatar von 2501
2501

Ströme in C1 sind Thread-sicher2. Funktionen müssen den Stream sperren, bevor darauf zugegriffen werden kann3.

Die fflush-Funktion ist Thread-sicher und kann jederzeit von jedem Thread aufgerufen werden, solange der Stream ein Ausgabestream oder ein Aktualisierungsstream ist4.


1 Gemäß dem aktuellen Standard, der C11 ist.

2 (Zitiert aus: ISO/IEC 9899:201x 7.21.3 Streams 7)
Jeder Stream hat eine zugeordnete Sperre, die verwendet wird, um Datenrennen zu verhindern, wenn mehrere Ausführungs-Threads auf einen Stream zugreifen, und um die Verschachtelung von Stream-Operationen einzuschränken, die von mehreren Threads ausgeführt werden. Diese Sperre kann jeweils nur von einem Thread gehalten werden. Die Sperre ist ablaufinvariant: Ein einzelner Thread kann die Sperre zu einem bestimmten Zeitpunkt mehrmals halten.

3 (Zitiert aus: ISO/IEC 9899:201x 7.21.3 Streams 8)
Alle Funktionen, die die Position eines Streams lesen, schreiben, positionieren oder abfragen, sperren den Stream, bevor sie darauf zugreifen. Sie geben die dem Stream zugeordnete Sperre frei, wenn der Zugriff abgeschlossen ist. reentrant: Ein einzelner Thread kann die Sperre mehrmals zu einem bestimmten Zeitpunkt halten.

4 (Zitiert aus: ISO/IEC 9899:201x 7.21.5.2 The fflush function 2)
Wenn stream auf einen Ausgabestream oder einen Aktualisierungsstream zeigt, in den die letzte Operation nicht eingegeben wurde, bewirkt die fflush-Funktion, dass alle ungeschriebenen Daten für diesen Stream, die an die Hostumgebung geliefert werden sollen, in die Datei geschrieben werden; andernfalls ist das Verhalten undefiniert.

  • Befolgt der MSVC-Compiler diese unter x86/x86_64 Windows?

    – Serge Rogatch

    20. August 2016 um 14:24 Uhr

  • @cat VS ist nicht einmal C99-konform.

    – 2501

    20. August 2016 um 14:35 Uhr

  • @2501 Eheheh, na, welche Hoffnung gibt es für das Universum? Vielleicht sollten sie ihre Benutzer einfach dazu ermutigen, stattdessen Clang zu verwenden.

    – Katze

    20. August 2016 um 14:42 Uhr

  • C-Streams waren nicht immer sicher. Ich erinnere mich, dass ich einen Fehler aufspüren musste, wo sie nicht waren. Trotzdem, wenn Sie heute auf einen solchen Fehler stoßen, ärgern Sie Ihren Compiler-Anbieter.

    – Josua

    20. August 2016 um 15:53 ​​Uhr


  • @cat Microsoft Visual C++ zielt darauf ab, ein C++-Compiler zu sein, kein C-Compiler. AFAIK, sie unterstützen C++14 in neueren Versionen.

    – Benutzer253751

    21. August 2016 um 4:25 Uhr

Die POSIX.1- und C-Funktionen, die auf Zeichenströmen (dargestellt durch Zeiger auf Objekte des Typs FILE) arbeiten, sind erforderlich nach POSIX.1c so zu implementieren, dass Wiedereintritt erreicht wird (siehe ISO/IEC 9945:1-1996, §8.2).

Diese Anforderung hat einen Nachteil; aufgrund der Synchronisation, die in die Implementierungen der Funktionen zum Zwecke der Wiedereintrittsfähigkeit eingebaut werden muss, führt dies zu erheblichen Leistungseinbußen. POSIX.1c adressiert diesen Kompromiss zwischen Wiedereintritt (Sicherheit) und Leistung durch die Einführung von hochleistungsfähigen, aber nicht wiedereintrittsfähigen (potentiell unsicheren) Versionen der folgenden Standard-I/O-Funktionen der Sprache C: getc(), getchar(), putc () und putchar(). Die nicht wiedereintrittsfähigen Versionen heißen getc_unlocked() usw., um ihre Unsicherheit zu betonen.

Beachten Sie jedoch, wie andere angemerkt haben: Viele beliebte Systeme (einschließlich Windows und Android) sind nicht POSIX-kompatibel.

  • ▲ Für „nicht POSIX-konform“, aber meinst du das? fflush nicht Thread-sicher ist, dass sie POSIX.1c nicht implementieren oder dass sie keine Konformität beanspruchen? Eine Liste (oder Link zu einer) von Plattformen (mit Versionsinformationen) und deren Konformität insofern könnte eine nützliche Ressource sein.

    – PJTrail

    24. August 2016 um 11:00 Uhr

  • Ich weiß nicht, ob Google das verspricht fflush wird Thread-sicher sein, auch wenn es aktuell so istaber Microsoft tut zumindest unter Windows.

    – geocar

    24. August 2016 um 11:03 Uhr

Benutzeravatar von chqrlie
chqrlie

Du sollst nicht anrufen fflush() Bei einem Eingabestream ruft es ein undefiniertes Verhalten auf, daher gehe ich davon aus, dass der Stream im Schreib- oder Aktualisierungsmodus geöffnet ist.

Wenn der Stream im Aktualisierungsmodus geöffnet ist ("w+" oder "r+"), darf der letzte Vorgang kein Lesevorgang sein, wenn Sie aufrufen fflush(). Da der Stream in verschiedenen Threads asynchron verwendet wird, wäre es schwierig, dies ohne irgendeine Form von Kommunikation zwischen Prozessen und Synchronisierung oder Sperren sicherzustellen, wenn Sie Lesevorgänge durchführen. Es gibt immer noch einen triftigen Grund, die Datei im Update-Modus zu öffnen, aber stellen Sie sicher, dass Sie nach dem Start keine Lesevorgänge durchführen fflush Faden.

fflush() ändert die aktuelle Position nicht. Es bewirkt lediglich, dass alle gepufferten Ausgaben in das System geschrieben werden. Die Streams sind in der Regel durch eine Sperre geschützt, also aufrufen fflush() in einem Thread sollte die Ausgabe eines anderen Threads nicht durcheinander bringen, aber es kann das Timing der Systemschreibvorgänge ändern. Wenn mehrere Threads dasselbe ausgeben FILE*, ist die Reihenfolge, in der die Verschachtelung erfolgt, ohnehin unbestimmt. Darüber hinaus, wenn Sie verwenden fseek() Sie haben verschiedene Threads für denselben Stream muss Verwenden Sie eine eigene Sperre, um die Konsistenz zwischen den sicherzustellen fseek() und die folgende Ausgabe.

Obwohl es in Ordnung scheint, dies zu tun, wird es wahrscheinlich nicht empfohlen. Du könntest stattdessen anrufen fflush() nach den Schreibvorgängen in jedem Thread, bevor die Sperre freigegeben wird.

Ziemlich einfache Antwort, das dürfen Sie nicht tun, da die Datei eine einzige “aktuelle Position” hat. Wie behalten Sie den Überblick? Wird die Datei für sequenziellen Zugriff oder zufällig geöffnet? Im letzteren Fall könnten Sie es mehrmals öffnen (einmal für jeden Thread) und dennoch Wege finden, die Dateistruktur konsistent zu halten.

Die eigentliche Antwort scheint zu sein, dass Streams selbst Thread-sicher sind (sollten), aber wenn das nicht der Fall wäre, könnte Ihr Problem das sein fflush tritt (außerhalb einer Sperre) auf, während ein anderer Thread ist Schreiben (innerhalb eines kritischen Abschnitts).

Daher würde ich mit dem Modell der virtuellen Maschine arbeiten, gegen die Sie codieren, und die fflush() auch in einem kritischen Abschnitt.

1404920cookie-checkKann ein Programm gleichzeitig fflush() für dieselbe FILE* aufrufen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy