public async Task RunAsync(PatchInfo[] toUpdate, PatchCache patchCache, CancellationToken ct = default) { Progress.Progress = 0; Progress.IsIndeterminate = true; Progress.CompletedCount = 0; Progress.TotalCount = 0; if (toUpdate.Length == 0) { return; } var state = new ProcessState { AtomicIndex = 0, AtomicCompletedCount = 0, AtomicProcessCount = 0, UpdateBuckets = new ConcurrentQueue <List <PatchCacheEntry> >() }; var processCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(ct); Progress.TotalCount = toUpdate.Length; App.Logger.Info(nameof(VerifyFilesPhase), "Starting processing threads"); Progress.IsIndeterminate = false; // TODO: processor affinity? var threadTasks = Enumerable.Range(0, Environment.ProcessorCount) .Select(i => ConcurrencyUtils.RunOnDedicatedThreadAsync(() => { Interlocked.Increment(ref state.AtomicProcessCount); try { App.Logger.Info(nameof(VerifyFilesPhase), $"Processing thread {i} started"); _Process(state, toUpdate, processCancellationTokenSource.Token); } catch (OperationCanceledException) { App.Logger.Error(nameof(VerifyFilesPhase), $"Processing thread {i} canceled"); processCancellationTokenSource.Cancel(); throw; } catch (Exception ex) { App.Logger.Error(nameof(VerifyFilesPhase), $"Exception in processing thread {i}", ex); processCancellationTokenSource.Cancel(); throw; } finally { Interlocked.Decrement(ref state.AtomicProcessCount); App.Logger.Info(nameof(VerifyFilesPhase), $"Processing thread {i} ended"); } }, $"{nameof(VerifyFilesPhase)}({i})")).ToArray(); await Task.Run(async() => { while (state.AtomicProcessCount > 0 || state.UpdateBuckets.Count != 0) { await Task.Delay(250, ct); Progress.Progress = state.AtomicCompletedCount / (double)toUpdate.Length; Progress.CompletedCount = state.AtomicCompletedCount; if (state.UpdateBuckets.Count == 0) { continue; } while (state.UpdateBuckets.TryDequeue(out var list)) { if (list.Count > 0) { await patchCache.InsertUnderTransactionAsync(list); } } } }); Progress.IsIndeterminate = true; App.Logger.Info(nameof(VerifyFilesPhase), "Joining processing threads"); try { await Task.WhenAll(threadTasks); } catch (Exception ex) { App.Logger.Error(nameof(VerifyFilesPhase), "Error verifying files", ex); throw; } }
public DiscordManager() { _DisposedTokenSource = new CancellationTokenSource(); App.Logger.Info(nameof(DiscordManager), "Discord manager created"); App.Current.Dispatcher.Invoke(async() => await ConcurrencyUtils.RunOnDedicatedThreadAsync(_BackgroundLoop)); }