Ich habe einen Thread, der in einer Sperre sitzt recv()
Schleife und ich möchte beenden (angenommen, dies kann nicht geändert werden select()
oder jeder andere asynchrone Ansatz).
Ich habe auch einen Signal-Handler, der fängt SIGINT
und theoretisch sollte es machen recv()
Rückkehr mit Fehler und errno
einstellen EINTR
.
Aber das tut es nicht, was meiner Meinung nach etwas damit zu tun hat, dass die Anwendung Multithreading ist. Es gibt auch noch einen Thread, der mittlerweile auf einen wartet pthread_join()
Anruf.
Was passiert hier?
BEARBEITEN:
OK, jetzt gebe ich das Signal explizit an alle Blocker weiter recv()
Fäden über pthread_kill()
aus dem Hauptthread (was zu derselben globalen SIGINT
Signal-Handler installiert, obwohl mehrere Aufrufe harmlos sind). Aber recv()
Anruf ist immer noch nicht entsperrt.
BEARBEITEN:
Ich habe ein Codebeispiel geschrieben, das das Problem reproduziert.
- Der Haupt-Thread verbindet einen Socket mit einem sich schlecht benehmenden Remote-Host, der die Verbindung nicht loslässt.
- Alle Signale blockiert.
- Thread lesen Thread wird gestartet.
- Main entsperrt und installiert Handler für
SIGINT
. - Lesen Sie Thread entsperrt und installiert Handler für
SIGUSR1
. - Der Signalhandler des Haupt-Threads sendet a
SIGUSR1
zum Lesethread.
Interessanterweise, wenn ich ersetzen recv()
mit sleep()
es wird einfach unterbrochen.
PS
Alternativ können Sie einfach einen UDP-Socket öffnen, anstatt einen Server zu verwenden.
Klient
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
static void
err(const char *msg)
{
perror(msg);
abort();
}
static void
blockall()
{
sigset_t ss;
sigfillset(&ss);
if (pthread_sigmask(SIG_BLOCK, &ss, NULL))
err("pthread_sigmask");
}
static void
unblock(int signum)
{
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, signum);
if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL))
err("pthread_sigmask");
}
void
sigusr1(int signum)
{
(void)signum;
printf("%lu: SIGUSR1\n", pthread_self());
}
void*
read_thread(void *arg)
{
int sock, r;
char buf[100];
unblock(SIGUSR1);
signal(SIGUSR1, &sigusr1);
sock = *(int*)arg;
printf("Thread (self=%lu, sock=%d)\n", pthread_self(), sock);
r = 1;
while (r > 0)
{
r = recv(sock, buf, sizeof buf, 0);
printf("recv=%d\n", r);
}
if (r < 0)
perror("recv");
return NULL;
}
int sock;
pthread_t t;
void
sigint(int signum)
{
int r;
(void)signum;
printf("%lu: SIGINT\n", pthread_self());
printf("Killing %lu\n", t);
r = pthread_kill(t, SIGUSR1);
if (r)
{
printf("%s\n", strerror(r));
abort();
}
}
int
main()
{
pthread_attr_t attr;
struct sockaddr_in addr;
printf("main thread: %lu\n", pthread_self());
memset(&addr, 0, sizeof addr);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket < 0)
err("socket");
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) <= 0)
err("inet_pton");
if (connect(sock, (struct sockaddr *)&addr, sizeof addr))
err("connect");
blockall();
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (pthread_create(&t, &attr, &read_thread, &sock))
err("pthread_create");
pthread_attr_destroy(&attr);
unblock(SIGINT);
signal(SIGINT, &sigint);
if (sleep(1000))
perror("sleep");
if (pthread_join(t, NULL))
err("pthread_join");
if (close(sock))
err("close");
return 0;
}
Server
import socket
import time
s = socket.socket(socket.AF_INET)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1',8888))
s.listen(1)
c = []
while True:
(conn, addr) = s.accept()
c.append(conn)
Ich stoße auf etwas Ähnliches (obwohl es in gewisser Hinsicht anders ist) mit einer Single-Thread-Anwendung (einem einzelnen Prozess ohne anderen Thread). Daher glaube ich, dass Multithreading hier nicht die Ursache für das Verhalten ist.
– Hibou57
30. März 2013 um 19:25 Uhr