This is one of the difficult problems. When you exit the application, all other threads should exit their thread functions, otherwise they will hold your application process from terminating.
The problem appears when the thread function "never returns", which is a very usual case.
The usual technique is returning the thread function in cooperative manner. In other words, the thread should exit this function by itself. One way to do it is having the "exit flag" variable set by the UI thread and checked by the other thread(s) periodically. In more complex cases, the check is done in the functions deeper on stack; then the perfect way to unwind the stack would be throwing a special "exit" exception (any distinct object thrown would be fine; all you need from it is being different from all other cases of exceptions); by the way, all exceptions are usually should be caught of the very top stack frame of the stack of each thread.
I do understand how ugly this solution is, but this is the usual technique. Polling is always a performance hit and a development/support hassle. But other techniques are considered dangerous.
Now, there is an advanced technique of safe thread abort developed and published by one Microsoft developer. Unfortunately, I don't have a reference at this time, but I implemented it and know every detail of implementation and functionality. Very few people know that work, but the approach itself eventually made the way to .NET which you don't use. It uses preemptive nature of the Windows threading. I mention it for completeness but don't want to explain it right now, because: 1) I don't believe you are going to implement it in practice, this is a very low-level method; 2) the approach is based on fully documented but not portable Windows API; in other words, you would need to have different implementations for different CPUs; 3) the safety of this approach used to cause furious flame wars (even in discussion of much more stable .NET thread abortion) I don't want to get in; I am personally sure that the method is very safe and reliable, but I will agree that even the use of it requires very good understanding of the technique by the developer using it, otherwise the technique will cause the trouble; my ultimate argument in favor of the technique is that there are many other techniques which also require deep understanding and are dangerous otherwise, but still they don't cause flame wars.
If you are curious, I'll give you more detail.[EDIT]
See also my past answer:
Close correcly the thread inside a dll[
^].
—SA