Der Schlüssel zum Erfolg ist, dass man sicherstellt, dass mit dem ReadThread tatsächlich nur gelesen wird und mit dem WriteThread nur geschrieben wird. Das wiederum ist mit
Indy nicht so trivial, weil die diversen Methoden verdecken, was tatsächlich geschieht. Während des Verbindungsaufbau/Abbau darf nur einer der Threads auf den Client zugreifen. Das ist hier nicht der Fall, denn Du unterbrichst die Verbindung, während der ReadThread versucht zu lesen.
Mein Vorschlag: Du baust beim ReadThread einen Timeout von z.B. 20ms [IOHandler.Readable(20)] ein. Du wartest also nicht unendlich lange auf Daten, sondern halt nur kurze Zeit. Nach Ablaufen des Timeouts, prüfst Du ob der Thread terminiert wurde. Wenn er nicht terminiert wurde, darfst Du nochmal versuchen zu lesen usw.
Das regelmäßige Aufwachen des Threads wegen des Timeouts führt zu keiner nennenswerten CPU-Belastung. Für den Verbindungsabbau terminierst Du den ReadThread. Dann wartest Du bis er tatsächlich beendet wurde. Erst jetzt löst Du Disconnect aus.