Ich habe so ein ähnliches Problem vor geraumer Zeit gehabt. Als erstes habe ich die
Indy-UDP-Komponenten entsorgt und eine Dritt-Implementation von UDP eingesetzt (UdpSockUtil, zu finden auf entwickler-ecke.de).
Im Empfängerthread wird dann eine TQueue mit den Empfangsdaten gefüllt. Nach jedem Empfang wird mit Postmessage der Hauptthread verständigt. Dieser wiederum liest dann solange Daten aus der Queue, bis diese leer ist.
Ich habe die Synchronisation manuell mit TCriticalSections gemacht, so das ich exakte Kontrolle darüber habe, wann wer wie wo was liest oder schreibt - vor allem blockiere ich so die Queue
nur für den einzelnen Lese/Schreibvorgang und gebe sie sofort wieder "frei".
Falls die Windows-Messagequeue überzulaufen beginnt, kann man die Postmessages einfach auf jeden zweiten/dritten Eintrag begrenzen. Der Hauptthread arbeitet dann eh alles ab.