Let's say you have a list of files you want to transfer in parallel (one thread for each file) and want to wait until all the uploads are finished, you could create an array of WaitHandle
s (number of wait handles == number of files == number of threads) and pass each thread one of them (wait handle : thread == 1:1) along with the file the thread should transfer.
The thread would transfer the file and before it exists, it would set the wait handle.
In the "main thread" you could wait for all the wait handles from the array to be set using WaitHandle.WaitAll(WaitHandle[])
(I may not be correct about the exact syntax here, but a command like this exists, I'm sure. I've used it before).
This is called a barrier.