По Threadам особо не думал над этим. Почему бы тот MessageBox не показывать в том же самом методе calculate?
Если требуется обращение к GUI - то Invoke в помощь.
А вообще, в современном C# для асинхронности есть задачи (Task) и async-await, и вот await по определению как раз ждет, когда задача выполнится.
Вот типа такого писал (внимание на слово "async" - оно обязательно):
private async void button1_Click(object sender, EventArgs e)
{
var s = await Task<string>.Factory.StartNew(() => {
/* Этот код выполняется асинхронно - в отдельном потоке */
return "Test";
});
/* А этот код снова в том же потоке */
MessageBox.Show(s);
}
VS была 2013. Но .NET - не 4.5, а 4.0, подключил NuGetовский пакет Microsoft Async.