Parallel Threading Tasks With Status

madaxe2023

New member
Joined
Apr 9, 2023
Messages
3
Programming Experience
5-10
I'm creating a WPF Splash Screen that should display a progress bar while the background data is being loaded. I want this to be parallel processed.

My first attempt sort of works as long as the same method is being used x number of times.

the second method is closer to what i want but how do i report progress.

thanks

madaxe

C#:
private Task LoadDataTask(IProgress<ProgressTracker> progress,CancellationToken cancellationToken)
        {
            ProgressTracker report = new ProgressTracker();

            Parallel.ForEach<int>(endPoints, (call) =>
            {
                results.Add(_configFileBuilderHTTPClient.GetAllAppConfigModelsAsync().Result);
                report.percentageComplete = (results.Count * 100) / endPoints.Count;
                cancellationToken.ThrowIfCancellationRequested();
                progress.Report(report);
            });
            report.percentageComplete = 100;
            progress.Report(report);
            return Task.CompletedTask;
        }

C#:
private Task LoadDataTask2(IProgress<ProgressTracker> progress, CancellationToken cancellationToken)
        {
            ProgressTracker report = new ProgressTracker();

            List<AppConfigModel>? intResult1 = null;
            List<AppConfigModel>? intResult2 = null;
            List<AppConfigModel>? intResult3 = null;

            Parallel.Invoke(
                () => intResult1 = _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync().Result,
                () => intResult2 = _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync().Result,
                () => intResult3 = _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync().Result
            );

            return Task.CompletedTask;
        }
 
But you'll just end up with the same ugly code if you need to update progress and check for cancellation.

Each of your lines 10-12 would change from:
C#:
() => intResult1 = _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync().Result
to
C#:
() => {
    intResult1 = _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync().Result;
    report.percentageComplete = (results.Count * 100) / endPoints.Count;
    cancellationToken.ThrowIfCancellationRequested();
    progress.Report(report);
}
 
Also, it's usually a bad practice to be pulling the the Result from an asynchronous method. In most cases, you'll want to use await to get the results.
 
This is what I have after struggling for a few days, it may not be perfect but it works, I will want to call different async methods to get different data for my main application while the splash screen is displayed.

I would love some constructive, supporting feedback, from anybody that can help me learn as a self taught developer, with examples or even a rehash I where I'm at.

just for reference I usually inherit my on notify change parts, and inject my httpclient and helper class.

thanks

Madaxe

C#:
namespace WpfApp3
{
    public interface ISplashScreenViewModel
    {
        void loadData(Window window);
        int ProgressBarValue { get; set; }
        ICommand? CancelLoadCommand { get; set; }
        List<List<AppConfigModel>?> results { get; set; }
    }

    public class SplashScreenViewModel : ISplashScreenViewModel,INotifyPropertyChanged
    {
        private Window? _window=null;

        public event PropertyChangedEventHandler? PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
        private ConfigFileBuilderHTTPClient _configFileBuilderHTTPClient = new ConfigFileBuilderHTTPClient();

        private int _progressBarValue = 0;
        public int ProgressBarValue
        {
            get => _progressBarValue;
            set
            {
                _progressBarValue = value;
                OnPropertyChanged(nameof(ProgressBarValue));
            }
        }

        public ICommand? CancelLoadCommand { get; set; } = null;

        public List<List<AppConfigModel>?> results { get; set; } = new List<List<AppConfigModel>?>() { };

        public void loadData(Window window)
        {
            _window = window;

            Progress<ProgressTracker> progress = new Progress<ProgressTracker>();
            progress.ProgressChanged += ReportProgress;

            CancelLoadCommand = new CommandsBase(ExecuteCancelLoadCommand);

            try
            {
                Task.Run(() => { LoadDataTask(progress, _cancellationTokenSource.Token); });
            }
            catch(OperationCanceledException)
            {
               _window.Close();
            }
        }

        private List<List<AppConfigModel>?> LoadDataTask(IProgress<ProgressTracker> progress, CancellationToken cancellationToken)
        {
            ProgressTracker report = new ProgressTracker();

            List<Task<List<AppConfigModel>?>> myTaskList = new List<Task<List<AppConfigModel>?>>
            {
                _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync(),
                _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync(),
                _configFileBuilderHTTPClient.GetAllAppConfigModelsAsync()
            };

            var taskstoProcessList = myTaskList.Select(x => AwaitAndProcessAsync(x, myTaskList.Count, report, progress, cancellationToken)).ToList();
            var resultsList = Task.WhenAll(myTaskList).Result.ToList<List<AppConfigModel>?>();

            return resultsList;
        }

        async Task AwaitAndProcessAsync(
                                        Task<List<AppConfigModel>?> task,
                                        int TotalTaskNumber,
                                        ProgressTracker report,
                                        IProgress<ProgressTracker> progress,
                                        CancellationToken cancellationToken)
        {
            var result = await task;
            report.taskCount += 1;
            report.percentageComplete = (report.taskCount * 100) / TotalTaskNumber;
            cancellationToken.ThrowIfCancellationRequested();
            progress.Report(report);
        }

        private void ReportProgress(object? sender, ProgressTracker e)
        {
            ProgressBarValue = e.percentageComplete;
            if (e.percentageComplete == 100)
            {
                _window?.Close();
            }
        }

        private void ExecuteCancelLoadCommand(object? obj)
        {
            _cancellationTokenSource.Cancel();
        }
    }
}
 
Back
Top Bottom