private ICollection <FolderInfo> FinalizeFolders(ICollection <FolderInfo> pending, int importThreads) { List <FolderInfo> failed = new List <FolderInfo>(); ActionBlock <FolderInfo> processor = new ActionBlock <FolderInfo>(folderInfo => { if (this.Abort) { return; } Result r = Result.Skipped; try { ProgressTracker tracker = new ProgressTracker(folderInfo.Location, this.Log); if (!folderInfo.Modified && folderInfo.Exists && !tracker.Failed) { if (Log.IsDebugEnabled) { Log.Debug(string.Format("Skipping folder [{0}] - already completed", folderInfo.FullPath)); } IncreaseProgress(); return; } r = Result.Failed; Exception exc = null; try { ApplyMetadata(folderInfo, tracker); r = Result.Completed; tracker.DeleteOutcomeMarker(); } catch (Exception e) { exc = e; lock (failed) { failed.Add(folderInfo); } Log.Error(string.Format("Failed to apply the metadata to the folder at [{0}]", folderInfo.SafeFullPath), e); } finally { IncreaseProgress(); tracker.SaveOutcomeMarker(r, exc); } } finally { IncrementCounter(r); } }, new ExecutionDataflowBlockOptions { // MaxDegreeOfParallelism sets the maximum number of parallel processes MaxDegreeOfParallelism = importThreads, // We're constrained to a single producer thread, so set this to true SingleProducerConstrained = true, // Accept these many in the queue before bouncing/blocking? // BoundedCapacity = 1000, }); try { ResetProgress(this.Folders.Count); foreach (FolderInfo folder in pending) { if (this.Abort) { break; } processor.Post(folder); } return(failed); } finally { processor.Complete(); processor.Completion.Wait(); Log.Info(this.ProcessingReport); } }