The built in thread pools must have a limit.
Also, this isn't just a matter of keeping thread resources down, it's supporting scenarios like developing web and UI software.
For example, if I have "OnButtonClick" and it does async work, I'd like to also call other functions that may also touch UI components. WPF has support for binding async actions to UI events, and it eliminates the need to ever wonder what thread you are on.
In short, you can't "join" a thread on the UI thread. Proper async/await with a task scheduler is invaluable.
Also, C# has thread pools. Most people don't create a raw thread using "new Thread()". They would normally use "Task.Factory" or "Task.Run". If it is a long running thread, you can specify to Task.Factory that is it long running and it will remove your thread from the thread pool and put another one in it's place.