Как правильно дождаться выполнения всех потоков, созданных в цикле?
У Вас не правильный подход. Во первых забудьте про Thread и используйте TPL. Во вторых для распараллеливания запросов в цикле есть замечательный метод Parallel.ForEach. В третьих для ожидания выполнения всех параллельных задач есть Task.WaitAll.
В общем Вы можете создать список Task-ов и запихнуть их в Task.WaitAll, но лучше пройдитесь параллельным форичем по Вашему периоду и внутри запускайте Ваши методы. Код за форичем будет ожидать завершения всех потоков в цикле.
оффтоплюди подписались чисто поглазеть или поржать?
вы уверены, что вам нужен Thread?Task или Thread в C#.NET 4.0 WPF? ( Павел Елизарьев доступно описал разницу в ответе)
я бы использовал Task для начала, т.к. есть такая замечательная штука, как пулл потоков (подзабыл как на английском называется) который лучше обычного программиста (автоматически) понимает, когда и кто закончил, сколько есть свободных, сколько нужно и что с ним дальше делать. И нужно у вас в коде делать акцент не на блокирование/синхронизацию потоков (оставьте их в покое - чем быстрее сделают, тем лучше), а использовать вот такую простую штуку:  Оператор lock
а зависание происходит потому, что вы и основной поток блокируете.Ключевое слово lock не позволит ни одному потоку войти в важный раздел кода в тот момент, когда в нем находится другой поток. При попытке входа другого потока в заблокированный код потребуется дождаться снятия блокировки объекта.