private async Task LoadStocksFromStocksService() { try { var service = new StocksService(); var data = await service.GetStockPricesFor(Ticker.Text, cancellationTokenSource.Token); Stocks.ItemsSource = data; } catch (Exception ex) { Notes.Text += ex.InnerException.Message + Environment.NewLine; } }
private async Task ProcessStocksOnArrival() { try { var tickers = Ticker.Text.Split(',', ' '); var service = new StocksService(); var stocks = new ConcurrentBag <StockPrice>(); var tickerLoadingTasks = new List <Task <IEnumerable <StockPrice> > >(); foreach (var ticker in tickers) { var loadtask = service.GetStockPricesFor(ticker, cancellationTokenSource.Token) .ContinueWith(t => { foreach (var stock in t.Result.Take(5)) { // stocks collection can be safely used with different threads stocks.Add(stock); } Dispatcher.Invoke(() => { Stocks.ItemsSource = stocks.ToArray(); }); return(t.Result); }); tickerLoadingTasks.Add(loadtask); } await Task.WhenAll(tickerLoadingTasks); } catch (Exception ex) { Notes.Text += ex.Message + Environment.NewLine; } finally { cancellationTokenSource = null; } }
private async void Search_Click(object sender, RoutedEventArgs e) { #region Before loading stock data var watch = new Stopwatch(); watch.Start(); StockProgress.Visibility = Visibility.Visible; StockProgress.IsIndeterminate = true; Search.Content = "Cancel"; #endregion #region Cancellation if (cancellationTokenSource != null) { cancellationTokenSource.Cancel(); cancellationTokenSource = null; return; } cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.Token.Register(() => { Notes.Text += "Cancellation requested" + Environment.NewLine; }); #endregion try { #region Load One or Many Tickers var tickers = Ticker.Text.Split(',', ' '); var service = new StocksService(); var tickerLoadingTasks = new List <Task <IEnumerable <StockPrice> > >(); foreach (var ticker in tickers) { var loadTask = service.GetStockPricesFor(ticker, cancellationTokenSource.Token); tickerLoadingTasks.Add(loadTask); } #endregion // Stocks loaded - now need processing var loadedStocks = await Task.WhenAll(tickerLoadingTasks); // Use threadsafe collection var values = new ConcurrentBag <StockCalculation>(); // Standard foreach loop converted into a Parallel foreach // Includes guidance on max number of concurrent tasks var exResult = Parallel.ForEach(loadedStocks, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (stocks, state) => { var stockName = stocks.First().Ticker; // Will break out of current thread // Will not execute outstanding iterations // On-going tasks will not be stopped // return - explicitly return from current thread context if (stockName == "MBI") { state.Break(); return; } var result = CalculateExpensiveComputation(stocks); var data = new StockCalculation { Ticker = stocks.First().Ticker, Result = result }; values.Add(data); }); if (exResult.IsCompleted) { Stocks.ItemsSource = values; } else { Notes.Text = "Parallel computation of stocks did not complete successfully"; } // Calculate total stock price for each ticker decimal totalStocksValue = 0; exResult = Parallel.For(0, loadedStocks.Length, i => { var value = 0m; foreach (var s in loadedStocks[i]) { // 1. Unstable: totalStocksValue += Compute(s); // 2. Fix, but limited solution: // Interlocked.Add(ref totalStocksValue, (int)Compute(s)); // 3. Minimise code inside lock value += Compute(s); } lock (syncRoot) { totalStocksValue += value; } }); Notes.Text = $"Stock price total: {totalStocksValue:N2}"; } catch (Exception ex) { Notes.Text += ex.Message + Environment.NewLine; } finally { cancellationTokenSource = null; } #region After stock data is loaded StocksStatus.Text = $"Loaded stocks for {Ticker.Text} in {watch.ElapsedMilliseconds}ms"; StockProgress.Visibility = Visibility.Hidden; Search.Content = "Search"; #endregion }