/// <summary> /// Populates the tile cache for the configured tiled map definition or tile set /// </summary> /// <param name="progress"></param> /// <returns></returns> public TileSeedStats Run(IProgress <TileProgress> progress = null) { var failed = 0; var rendered = 0; var sw = new Stopwatch(); sw.Start(); Action <Action <TileRef>, TileRef> executor = _options.Executor ?? DefaultExecutor; var maxThreads = _options.MaxDegreeOfParallelism; var resId = _walker.ResourceID; var tiles = _walker.GetTileList(); var total = tiles.Length; var interval = 1000; //Every second using (new Timer(_ => progress?.Report(new TileProgress(rendered, total, failed)), null, interval, interval)) { Action <TileRef> fetcher = tile => { try { using (_tileSvc.GetTile(resId, tile.GroupName, tile.Col, tile.Row, tile.Scale)) { Interlocked.Increment(ref rendered); } } catch (Exception ex) { _options.ErrorLogger?.Invoke(tile, ex); Interlocked.Increment(ref failed); } }; if (maxThreads.HasValue) { var popts = new ParallelOptions(); popts.MaxDegreeOfParallelism = maxThreads.Value; Parallel.ForEach(tiles, popts, t => executor(fetcher, t)); } else { Parallel.ForEach(tiles, t => executor(fetcher, t)); } } // And this method blocks! So if we get to this point, everything has been iterated through and // this the tiling run has finished. sw.Stop(); return(new TileSeedStats { ResourceID = resId, TilesRendered = rendered, Duration = sw.Elapsed }); }
public async Task <TileSeedStats> RunAsync(IProgress <TileProgress> progress = null) { var failed = 0; var rendered = 0; var sw = new Stopwatch(); sw.Start(); Action <Action <TileRef>, TileRef> executor = _options.Executor ?? DefaultExecutor; var maxThreads = _options.MaxDegreeOfParallelism; var resId = _walker.ResourceID; var tiles = _walker.GetTileList(); var total = tiles.Length; var interval = 1000; //Every second using (new Timer(_ => progress?.Report(new TileProgress(rendered, total, failed)), null, interval, interval)) { Func <TileRef, Task> fetcher = async tile => { try { using (var tileStream = await _tileSvc.GetTileAsync(resId, tile.GroupName, tile.Col, tile.Row, tile.Scale)) { _options.SaveTile?.Invoke(tile, tileStream); Interlocked.Increment(ref rendered); } } catch (Exception ex) { _options.ErrorLogger?.Invoke(tile, ex); Interlocked.Increment(ref failed); } }; var parallelism = Environment.ProcessorCount * 2; if (maxThreads.HasValue) { parallelism = maxThreads.Value; } var fetchTasks = new List <Task>(parallelism); if (this.RandomizeRequests) { Shuffle(tiles); } //For the async version, we will loop through all the tiles //we need to fetch and load up an intermediate list of async tasks //(up to the specified parallelism limit) and then await the whole //lot once we hit that limit. Once we await that lot, we clear the //intermediate list and fill it up with the next batch. Rinse //and repeat until we've gone through all the tiles. foreach (var tr in tiles) { fetchTasks.Add(fetcher(tr)); if (fetchTasks.Count == parallelism) { await Task.WhenAll(fetchTasks); fetchTasks.Clear(); } } if (fetchTasks.Count > 0) { await Task.WhenAll(fetchTasks); fetchTasks.Clear(); } } sw.Stop(); return(new TileSeedStats { ResourceID = resId, TilesRendered = rendered, Duration = sw.Elapsed }); }