One very good use of threading is to uncouple different problem spaces. For example, if a single thread does remote communications, user interaction, and calculations, then as the program grows a few problems can arise. The user experience can change as communications slow down or your calculations grow in processor cycle consumption.
By separating the communications, user, and algorithmic portions of your code (a short list, especially if you are doing real-time systems) into their own threads, you increase the modularity and understandability of the code, you decrease the likelihood of introducing bugs due to unforeseen consequences, and you gain control over execution priority and synchronization.
A recent application that I worked on used threads for date-time management, communications, GUI, periodic remote requests, motion control, and watchdogs.
Of course, multithreading does come with its own issues, especially race conditions and things happening out of expected sequence, but these are manageable with semaphore, mutexes, and the like.