YouTo solve the initial problem, is you should be using await Task.WhenAll not Task.WaitAll
Creates a task that will complete when all of the supplied tasks have completed.
However, this looks like more of a job for Parallel.ForEach, or PLinq
Update Another issue is you looping over the same list twice (nested) which is a quadratic time complexity and is definitely not thread safe
YouAs a solve, you could probably change all your code tocreate a dictionary of the followingchanges, loop over the change set once (just as an examplein parallel), and update the references in one go.
_fileClasses = GetFileClasses(); // create a dictionary for fast lookup var changes = _fileClasses.Where(x => x.Filename != null && x.NewFilename != null) .ToDictionary(x => x.Filename, x => x.NewFilename); // parallel the workloads Parallel.ForEach(_fileClasses, (item) => { // iterate through the references for (var i = 0; i < item.FileReferences.Length; i++) { // check for updates if (changes.TryGetValue(item.FileReferences[i], out var value)) item.FileReferences[i] = value; } }); Note : This wasn't intended to be a complete solution as all the code wasn't supplied, however the time complexity should be a lot better