Vorgehensweise bei massivem Anstieg von offenen Handles

Diese Woche hatte ich bei einer Anwendung ein Problem mit einer schnell wachsenden Anzahl von offenen Handles (Was ist ein Handle?). Dieser Blogbeitrag soll Hilfe für Entwickler mit dem selben oder ähnlichen Problem sein.

Derzeit programmiere ich an einer Client-Server-Anwendung, bei der der Client ca. alle 10ms Daten über das HTTP-Protokoll vom Server anfordert.

Der Server ist in C++ für das Betriebssystem Windows implementiert. Bei einem Testbetrieb war im Windows-Task-Manager ein starker Anstieg der Handles zu erkennen. Der Task-Manager zeigte einen Zuwachs von etwa 50 Handles pro Sekunde an. Belastung des Prozessors und RAMs waren dagegen sehr gering. In einem Langzeittest über viele Stunden hat sich gezeigt, dass bei einer bestimmten Anzahl von Handles das System nicht mehr einwandfrei funktionierte. Was evtl. daran liegt, dass Windows nur eine bestimmte Anzahl von Handles pro Prozess zulässt (ich habe hier leider keine zuverlässige Quelle finden können, unter Win NT waren es wohl max. 10000 pro Prozess). 

In einem ersten Schritt ging es zu Identifizieren welcher Prozess für den Anstieg der Handles verantwortlich war. Als Schuldiger vermutete ich entweder Java-Script Code im Browser (Client) oder das eigentliche Serverprogramm.
Für solche Fälle ist der Process Explorer besonders hilfreich. Wenn man die Standardansicht um weitere Informationen erweitert, kann man zu jedem Prozess die Anzahl der Handles zur Laufzeit abrufen. Es war relativ eindeutig erkennbar, dass alleinig das Serverprogramm schuld an dem Zuwachs der Handles war.

Im zweiten Schritt sollte man sich klar machen um welche Art von Handles es sich handelt um so den potentiell fehlerhaften Code einschränken zu können. Mit dem Prozess Explorer kann man sich noch die jeweilige Kategorie anzeigen lassen, zu denen die offenen Handles des Prozesses gehören. In meinem Fall waren es Thread-Handles.

Bei jeder Anfrage an den Server wird ein neuer Thread erstellt, der diese Anfrage bearbeitet. Für den neuen Thread wird auch ein neues Handle erstellt.

Ich bin bisher davon ausgegangen, dass ein ExitThread()-Aufruf alle Handles zu einem Thread löscht. Nach Recherchieren in der MSDN ist mir bewusst geworden, dass dies nicht der Fall ist. Da ich die Funktion _beginthreadex(...) verwendet, sollte man vor dem Beenden der Thread-Funktion _endthreadex(...) aufrufen und das Handle mit Hilfe der Funktion CloseHandle(...) explizit löschen.

Hier nochmal als Codebeispiel:
// Threadfunktion
unsigned __stdcall ThreadFunction(void* arg) {
  // do something
  _endthreadex(0);
  return 0;
}

// "Aufruf"-Code
HANDLE handle = (HANDLE) _beginthreadex(0, 0, ThreadFunction, 0, 0, 0);
// do something and wait until thread has finished
CloseHandle(handle);

Insbesondere nach dem expliziten Schließen des Handles durch die CloseHandle-Funktion, die ich bisher mit jeder Art von Handles vewendet habe, jedoch nicht im Zusammenhang mit Threads, konnte das Problem gelöst werden. Die Anwendung läuft nun effizient, auch im Hinblick auf die Handles.

Trackback URL for this post:

http://www.mschoeffler.de/trackback/23

Kommentare

Alle 10ms wird ein neuer

Alle 10ms wird ein neuer Thread erstellt... skaliert das überhaupt?

Skalierung

Die Applikation ist recht speziell, im Normallfall hängen maximal zwei Clients am Server. Mit zwei Clients funktioniert das sehr zuverlässig. Alternativ kann man die Bearbeitung der Anfragen auch auf eine feste Anzahl von Threads umstellen. In diesem Fall wird nicht mehr pro Anfrage ein Thread erstellt, sondern eine feste Anzahl von Threads arbeitet nacheinander die Anfragen ab.

Danke für den Hinweis mit dem

Danke für den Hinweis mit dem Process-Explorer. Ich hatte ein anderes Problem mit Handles, konnte es nun aber lösen.
Viele Grüße aus der München!