Sockets auf Linux-Frage
Ich habe einen Worker-Thread, der bei einem Accept()-Aufruf blockiert wird. Es wartet einfach auf eine eingehende Netzwerkverbindung, verarbeitet sie und kehrt dann zum Lauschen auf die nächste Verbindung zurück.
Wenn das Programm beendet werden muss, wie signalisiere ich diesem Netzwerkarbeiter-Thread (vom Haupt-Thread), dass er vom Aufruf von accept() zurückkehrt, während ich immer noch in der Lage bin, seine Schleife ordnungsgemäß zu beenden und seinen Bereinigungscode zu verarbeiten.
Einige Dinge, die ich versucht habe:
-
pthread_kill, um ein Signal zu senden. Fühlt sich klumpig an, dies zu tun, und es erlaubt dem Thread nicht zuverlässig, seine Abschaltlogik auszuführen. Bewirkt auch, dass das Programm beendet wird. Signale möchte ich nach Möglichkeit vermeiden.
-
pthread_cancel. Das gleiche wie oben. Es ist ein harter Kill für den Thread. Das, und der Thread tut möglicherweise etwas anderes.
-
Schließen des Listen-Sockets aus dem Haupt-Thread, um accept() abzubrechen. Das funktioniert nicht zuverlässig.
Einige Einschränkungen:
Wenn die Lösung darin besteht, den Listen-Socket nicht blockierend zu machen, ist das in Ordnung. Aber ich möchte keine Lösung akzeptieren, bei der der Thread alle paar Sekunden über einen Auswahlaufruf aufwacht, um die Beendigungsbedingung zu überprüfen.
Die Thread-Bedingung zum Beenden darf nicht an das Beenden des Prozesses gebunden sein.
Im Wesentlichen sieht die Logik, die ich anstrebe, so aus.
void* WorkerThread(void* args)
{
DoSomeImportantInitialization(); // initialize listen socket and some thread specific stuff
while (HasExitConditionBeenSet()==false)
{
listensize = sizeof(listenaddr);
int sock = accept(listensocket, &listenaddr, &listensize);
// check if exit condition has been set using thread safe semantics
if (HasExitConditionBeenSet())
{
break;
}
if (sock < 0)
{
printf("accept returned %d (errno==%d)\n", sock, errno);
}
else
{
HandleNewNetworkCondition(sock, &listenaddr);
}
}
DoSomeImportantCleanup(); // close listen socket, close connections, cleanup etc..
return NULL;
}
void SignalHandler(int sig)
{
printf("Caught CTRL-C\n");
}
void NotifyWorkerThreadToExit(pthread_t thread_handle)
{
// signal thread to exit
}
int main()
{
void* ptr_ret= NULL;
pthread_t workerthread_handle = 0;
pthread_create(&workerthread, NULL, WorkerThread, NULL);
signal(SIGINT, SignalHandler);
sleep((unsigned int)-1); // sleep until the user hits ctrl-c
printf("Returned from sleep call...\n");
SetThreadExitCondition(); // sets global variable with barrier that worker thread checks on
// this is the function I'm stalled on writing
NotifyWorkerThreadToExit(workerthread_handle);
// wait for thread to exit cleanly
pthread_join(workerthread_handle, &ptr_ret);
DoProcessCleanupStuff();
}
pthread_kill()
sendet ein Signal an einen Thread, nicht mehr und nicht weniger. Und es muss nicht unbedingt “lässt das Programm enden“.– alk
4. Juli 2016 um 15:33 Uhr
“pthread_cancel. Das gleiche wie oben.„Äh, was? Nein,
pthread_cancel()
macht dann was ganz anderespthread_kill()
! Bitte RTFM.– alk
4. Juli 2016 um 15:34 Uhr
Ein einfacher Weg, um accept() zurückzugeben, wäre, eine TCP-Verbindung zu dem Port zu öffnen, auf dem der accept()-Aufruf lauscht.
– Jeremy Friesner
6. Dezember 2016 um 16:22 Uhr