I have what I assume is a pretty common threading scenario:
Microsoft's Reactive Framework is superb for this:
Action[] jobs = new Action[100];
var subscription =
jobs
.ToObservable()
.Select(job => Observable.Start(job))
.Merge(15)
.Subscribe(
x => Console.WriteLine("Job Done."),
() => Console.WriteLine("All Jobs Done."))
Done.
Just NuGet "System.Reactive".