Ich habe einen Worker-Thread, der einen TCP-Socket auf eingehenden Datenverkehr überwacht und die empfangenen Daten puffert, damit der Haupt-Thread darauf zugreifen kann (nennen wir diesen Socket EIN). Der Worker-Thread muss jedoch auch einige reguläre Operationen ausführen (z. B. einmal pro Sekunde), auch wenn keine Daten eingehen. Daher verwende ich select()
mit einem Timeout, damit ich nicht weiter abfragen muss. (Beachten Sie das Aufrufen receive()
auf einem nicht blockierenden Socket und dann für eine Sekunde schlafen, ist nicht gut: Die eingehenden Daten sollten sofort für den Haupt-Thread verfügbar sein, auch wenn der Haupt-Thread sie möglicherweise nicht immer sofort verarbeiten kann, daher die Notwendigkeit einer Pufferung. )
Jetzt muss ich auch in der Lage sein, dem Worker-Thread zu signalisieren, sofort etwas anderes zu tun; aus dem Hauptthread muss ich den Arbeitsthread machen select()
gleich wieder zurück. Ich habe das vorerst wie folgt gelöst (Ansatz grundsätzlich übernommen von hier und hier):
Beim Programmstart erstellt der Worker-Thread zu diesem Zweck einen zusätzlichen Socket vom Typ Datagramm (UDP) und bindet ihn an einen zufälligen Port (nennen wir diesen Socket B). Ebenso erstellt der Haupt-Thread einen Datagramm-Socket zum Senden. In seinem Aufruf an select()
listet der Worker-Thread jetzt beide auf EIN und B in dem fd_set
. Wenn der Haupt-Thread signalisieren muss, ist es sendto()
‘s ein paar Bytes an den entsprechenden Port weiter localhost
. Zurück im Worker-Thread, wenn B bleibt im fd_set
nach select()
kehrt dann zurück recvfrom()
aufgerufen und die empfangenen Bytes werden einfach ignoriert.
Das scheint sehr gut zu funktionieren, aber ich kann nicht sagen, dass mir die Lösung gefällt, hauptsächlich, weil dafür ein zusätzlicher Port gebunden werden muss Bund auch, weil es mehrere zusätzliche Socket-API-Aufrufe hinzufügt, die meiner Meinung nach fehlschlagen können – und ich habe nicht wirklich Lust, die geeignete Aktion für jeden der Fälle herauszufinden.
Ich denke, idealerweise möchte ich irgendeine Funktion aufrufen, die dauert EIN als Eingabe und tut nichts außer macht select()
gleich wieder zurück. Allerdings kenne ich eine solche Funktion nicht. (Ich denke, ich könnte z shutdown()
die Steckdose, aber die Nebenwirkungen sind nicht wirklich akzeptabel 🙂
Wenn dies nicht möglich ist, wäre die zweitbeste Option die Erstellung einer B Das ist viel dummer als ein echter UDP-Socket und erfordert nicht wirklich die Zuweisung begrenzter Ressourcen (über eine angemessene Menge an Speicher hinaus). ich vermute Unix-Domain-Sockets würde genau das tun, aber: Die Lösung sollte nicht viel weniger plattformübergreifend sein als das, was ich derzeit habe, wenn auch etwas moderater #ifdef
das Zeug ist in Ordnung. (Ich ziele hauptsächlich auf Windows und Linux ab – und schreibe nebenbei C++.)
Bitte schlagen Sie kein Refactoring vor, um die beiden separaten Threads loszuwerden. Dieses Design ist notwendig, da der Haupt-Thread möglicherweise für längere Zeit blockiert ist (z. B. durch intensive Berechnungen – und ich kann nicht regelmäßig aufrufen receive()
aus der innersten Rechenschleife), und in der Zwischenzeit muss jemand die eingehenden Daten puffern (und aus Gründen, die ich nicht beeinflussen kann, kann es nicht der Absender sein).
Jetzt, wo ich dies schreibe, ist mir klar geworden, dass jemand definitiv einfach antworten wird “Boost.Asio“, also habe ich es mir gerade zum ersten Mal angesehen … Ich konnte jedoch keine offensichtliche Lösung finden. Beachten Sie, dass ich auch nicht (leicht) beeinflussen kann, wie die Steckdose funktioniert EIN erstellt wird, aber ich sollte in der Lage sein, es bei Bedarf von anderen Objekten umhüllen zu lassen.