public sealed class AsyncDataLoader<T> { /// <summary> /// . /// </summary> public AsyncDataLoader() { EmergencyPeriod = TimeSpan.Zero; MinResultsCount = 0; } /// <summary> /// . /// </summary> /// <param name="dataLoaders">, .</param> /// <param name="loadDataPeriod">, .</param> public AsyncDataLoader(IEnumerable<Func<T>> dataLoaders, TimeSpan loadDataPeriod) : this(dataLoaders, loadDataPeriod, 0, TimeSpan.Zero) { } /// <summary> /// . /// </summary> /// <param name="dataLoaders">, .</param> /// <param name="loadDataPeriod">, .</param> /// <param name="minimalResultsCount"> .</param> /// <param name="emergencyPeriod">, .</param> public AsyncDataLoader(IEnumerable<Func<T>> dataLoaders, TimeSpan loadDataPeriod, int minimalResultsCount, TimeSpan emergencyPeriod) { DataLoaders = dataLoaders; LoadDataPeriod = loadDataPeriod; EmergencyPeriod = emergencyPeriod; MinResultsCount = minimalResultsCount; } /// <summary> /// , , . /// </summary> public TimeSpan EmergencyPeriod { get; set; } /// <summary> /// . /// </summary> public int MinResultsCount { get; set; } /// <summary> /// , . /// </summary> public IEnumerable<Func<T>> DataLoaders { get; set; } /// <summary> /// , . /// </summary> public TimeSpan LoadDataPeriod { get; set; } /// <summary> /// . /// </summary> public bool SkipDefaultResults { get; set; } /// <summary> /// . /// </summary> /// <returns> .</returns> public async Task<T[]> GetResultsAsync() { BlockingCollection<T> results = new BlockingCollection<T>(); List<Task> tasks = new List<Task>(); tasks.AddRange(DataLoaders.Select(handler => Task.Factory.StartNew(() => { T result = handler.Invoke(); results.Add(result); }, CancellationToken.None, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness, TaskScheduler.Default))); bool isAllCompleted = true; try { CancellationTokenSource source = new CancellationTokenSource(LoadDataPeriod); CancellationToken token = source.Token; #if DEBUG token = CancellationToken.None; // #endif await Task.Factory.ContinueWhenAll(tasks.ToArray(), (t) => { }, token); } catch (TaskCanceledException ex) // ? . { isAllCompleted = false; } if (!isAllCompleted && EmergencyPeriod > TimeSpan.Zero) // { Func<bool> isReadyHandler = () => results.Count >= MinResultsCount; //, , . await WaitWhenReady(isReadyHandler, EmergencyPeriod); } if (SkipDefaultResults) return results.Where(r => !object.Equals(r, default(T))).ToArray(); return results.ToArray(); } /// <summary> /// . /// </summary> /// <param name="isReadyValidator">, , .</param> /// <param name="totalDelay"> .</param> /// <param name="iterationsCount"> .</param> private async Task WaitWhenReady(Func<bool> isReadyValidator, TimeSpan totalDelay, int iterationsCount = 7) { if (isReadyValidator()) return; double milliseconds = totalDelay.TotalMilliseconds / iterationsCount; TimeSpan delay = TimeSpan.FromMilliseconds(milliseconds); for (int i = 0; i < iterationsCount; i++) { if (isReadyValidator()) return; await Task.Delay(delay); } } }
Source: https://habr.com/ru/post/245201/
All Articles