Как корректно отслеживать завершение потоков?
У функции pthread_join()  есть неблокирующий вариант pthread_tryjoin_np()
Предлагаемый POSIX вариант это join. Если не устраивает, то вы сами можете конструировать проверку завершения потоков теми средствами какие вам удобно и какие доступны.

Самое простое: выставляйте в потоке атомарный флаг завершения, в основном потоке проверяйте.
Если можно подождать какое-то время, то можно использовать условную переменную с таймером, а не флаг.
Можно и очередь, но, по моему, это как из пушки по воробьям. Для этой задачи не совсем то. С тем же успехом можно использовать pipe. Но зачем, когда это можно сделать проще.

Имейте ввиду, что join все равно надо вызвать, когда удостоверитесь, что поток завершен. Чтоб окончательно освободить все ресурсы потока. Или надо заранее делать поток отсоединенным (detach). Я бы использовал join, а не detach.