Stellen Sie sich vor, ich schreibe eine Bibliothek in C. Stellen Sie sich außerdem vor, dass diese Bibliothek aus einer Multithread-Umgebung verwendet werden soll. Wie mache ich es threadsicher? Genauer gesagt: Wie kann ich sicherstellen, dass bestimmte Funktionen jeweils nur von einem Thread ausgeführt werden?
Im Gegensatz zu Java oder C # zum Beispiel hat C keine Möglichkeit, Threads / Locks / etc. zu verarbeiten, ebenso wenig wie die C-Standardbibliothek. Ich weiß, dass Betriebssysteme Threads unterstützen, aber die Verwendung ihrer API würde die Kompatibilität meiner Bibliothek sehr einschränken. Welche Möglichkeiten habe ich, um meine Bibliothek so kompatibel / tragbar wie möglich zu halten? (zum Beispiel auf OpenMP oder auf Posix-Threads angewiesen, damit es mit mindestens allen Unix-ähnlichen Betriebssystemen kompatibel ist?)
Sie können Wrapper mit #ifdef erstellen. Es ist wirklich das Beste, was du tun kannst. (Oder Sie können eine Bibliothek von Drittanbietern verwenden, um dies zu tun).
Ich zeige, wie ich es als Beispiel für Windows und Linux gemacht habe. Es ist in C ++ und nicht in C, aber es ist nur ein Beispiel:
%Vor%Sie müssen die Threading-Bibliothek Ihres Betriebssystems verwenden. Auf Posix wird dies normalerweise pthreads sein, und Sie werden pthread_mutex_lock .
Windows verfügt über eine eigene Threading-Bibliothek und Sie sollten sich entweder ansehen kritische Abschnitte oder CreateMutex . Kritische Abschnitte sind zwar besser optimiert, aber auf einen einzelnen Prozess beschränkt, und Sie können sie nicht in WaitForMultipleObjects .
Sie haben zwei Hauptoptionen:
1) Sie geben an, in welche Multithread-Umgebung Ihre Bibliothek Thread-sicher ist und verwenden Sie die Synchronisationsfunktionen dieser Umgebung.
2) Sie geben an, dass Ihre Bibliothek nicht threadsicher ist. Wenn Ihr Aufrufer es in einer Multithread-Umgebung verwenden möchte, liegt es in seiner Verantwortung, es threadsicher zu machen, indem ggf. eine externe Synchronisierung verwendet wird, um alle Aufrufe an Ihre Bibliothek zu serialisieren. Wenn Ihre Bibliothek Handles verwendet und keinen globalen Status benötigt, könnte dies zum Beispiel bedeuten, dass wenn sie ein Handle haben, das sie nur in einem einzelnen Thread verwenden, sie keine Synchronisierung für dieses Handle benötigen, da es automatisch serialisiert wird.
Offensichtlich können Sie einen Multi-Pack-Ansatz für (1) wählen und Kompilierungszeitkonstanten verwenden, um alle Umgebungen zu unterstützen, die Sie kennen.
Sie können auch eine Callback-Architektur, eine Link-Zeit-Abhängigkeit oder Makros verwenden, damit Ihr Anrufer Ihnen sagen kann, wie man synchronisiert. Dies ist eine Art Mischung aus (1) und (2).
Aber es gibt keine standardmäßige Multithread-Umgebung, daher ist es ziemlich unmöglich, eigenständigen Code zu schreiben, der überall threadsicher ist, es sei denn, er ist komplett zustandslos (dh die Funktionen sind alle nebenwirkungsfrei) . Auch dann muss man "Nebenwirkung" großzügig interpretieren, da der C-Standard natürlich nicht definiert, welche Bibliotheksfunktionen Thread-sicher sind. Es ist ein bisschen so, als würde man fragen, wie man C-Code schreibt, der in einem Hardware-Interrupt-Handler ausgeführt werden kann. "Was ist ein Interrupt?", Könnten Sie sehr gut fragen, "und welche Dinge, die ich in C machen könnte, sind in einem nicht gültig?". Die einzigen Antworten sind OS-spezifisch.
Es ist ein Missverständnis, dass die Pthreads-Bibliothek unter Windows nicht funktioniert. Besuche sourceforge.net . Ich würde Pthreads empfehlen, weil es plattformübergreifend ist und seine Mutexe sind viel schneller als z. Die Windows eingebauten Mutexe.
Schreiben Sie Ihr eigenes Schloss.
Da Sie auf PCs ausgerichtet sind, haben Sie es mit der x86-Architektur zu tun, die nativ alle Multithreading-Unterstützung bietet, die Sie benötigen. Gehen Sie über Ihren Code und identifizieren Sie Funktionen, die Ressourcen freigegeben haben. Geben Sie jeder gemeinsam genutzten Ressource einen 32-Bit-Zähler. Anschließend wird mithilfe der von den CPUs implementierten Interlock-Operationen protokolliert, wie viele Threads jede freigegebene Ressource verwenden, und alle Threads, die eine gemeinsam genutzte Ressource verwenden möchten, warten, bis die Ressource freigegeben wird.
Hier ist ein wirklich guter Blogbeitrag über ineinandergreifende Operationen: Interlocked Instructions verwenden von C / C ++
Der Autor konzentriert sich hauptsächlich auf die Win32 Interlocked Wrapper, aber so ziemlich jedes Betriebssystem hat seine eigenen Wrapper für die Interlocked-Operationen, und Sie können immer die Assembly schreiben (jede dieser Operationen ist nur eine Anweisung).
Wenn Ihr Ziel unter Unix-ähnlichen Betriebssystemen kompatibel sein soll, würde ich POSIX-Threading verwenden.
Wenn Sie Windows ebenfalls unterstützen möchten, müssen Sie dafür zwei Codepfade verwenden: pthreads unter Unix und Windows-Threads unter Windows. Es ist ziemlich einfach, eine eigene "Thread-Bibliothek" zu erstellen, um diese zu umbrechen.
Es gibt einige, die das tun (wie OpenThreads ), aber die meisten, die ich verwendet habe, sind C ++, nicht C .
"Stellen Sie sich vor, ich schreibe eine Bibliothek in C. Stellen Sie sich vor, diese Bibliothek könnte aus einer Multithread-Umgebung verwendet werden. Wie mache ich sie threadsicher? Genauer gesagt: Wie kann ich bestimmte Funktionen sicherstellen? werden jeweils nur ein Thread ausgeführt? "
Sie können nicht - & gt; schreibe thread-sichere oder bessere Reentry-Funktionen. Es sei denn, Sie möchten systemweite Sperren schreiben - eine sehr schlechte Idee.
" Im Gegensatz zu Java oder C # zum Beispiel hat C keine Möglichkeit, mit Threads / Sperren / etc umzugehen. "
Das ist ein Witz - oder? Lange bevor Java und C # entwickelt wurden, wurden die Locks erfunden und als Synchronisationsobjekte weit verbreitet ...
"Ich weiß, dass Betriebssysteme Threads unterstützen, aber die Verwendung ihrer API würde die Kompatibilität meiner Bibliothek sehr einschränken."
Die Sache ist, dass solche Bibliotheken bereits existieren - z. wxWidgets, die das portable wxThread anbieten ... (aber das ist C ++)
Wie auch immer, es gibt zwei Hauptaromen von C: das ANSI C und das GNU C - & gt; zwei verschiedene Welten ... wähle das eine oder das andere aus.
Die Verwendung von Posix-Threads klingt für mich eine gute Idee (aber ich bin kein Experte). Insbesondere hat Posix gute Primitive, um den gegenseitigen Ausschluss sicherzustellen.
Wenn Sie eine Bibliothek ohne Abhängigkeiten erstellen müssten, müssten Sie die gegenseitigen Ausschlussalgorithmen selbst implementieren, was eine schlechte Idee ist.
Tags und Links c multithreading thread-safety