23.7 Displaying an Asynchronous Task’s Progress

Our last example shows how to display an asynchronous task’s progress and intermediate results. Figure 23.6 presents class FindPrimes, which asynchronously determines whether each value from 2 up to a user-entered value is a prime number. During the asynchronous testing of each value, we update a TextBox with each prime that’s found and update a ProgressBar and Label to show the percentage of the testing that has been completed so far.

Fig. 23.6 Displaying an asynchronous task’s progress and intermediate results.

Alternate View

   1   // Fig. 23.6: FindPrimes.cs
   2   // Displaying an asynchronous task's progress and intermediate results
   3   using System;
   4   using System.Linq;
   5   using System.Threading.Tasks;
   6   using System.Windows.Forms;
   7
   8   namespace FindPrimes
   9   {
  10      public partial class FindPrimesForm : Form
  11      {
  12         // used to enable cancelation of the async task
  13         private bool Canceled { get; set; } = false;
  14         private bool[] primes; // array used to determine primes
  15
  16         public FindPrimesForm()
  17         {
  18            InitializeComponent();
  19            progressBar.Minimum = 2; // 2 is the smallest prime number
  20            percentageLabel.Text = $"{0:P0}"; // display 0 %
  21         }
  22
  23         // handles getPrimesButton's click event
  24         private async void getPrimesButton_Click(object sender, EventArgs e)
  25         {
  26            // get user input
  27            var maximum = int.Parse(maxValueTextBox.Text);
  28
  29            // create array for determining primes
  30            primes = Enumerable.Repeat(true, maximum).ToArray();
  31
  32            // reset Canceled and GUI
  33            Canceled = false;
  34            getPrimesButton.Enabled = false; // disable getPrimesButton
  35            cancelButton.Enabled = true; // enable cancelButton
  36            primesTextBox.Text = string.Empty; // clear primesTextBox
  37            statusLabel.Text = string.Empty; // clear statusLabel
  38            percentageLabel.Text = $"{0:P0}"; // display 0 %
  39            progressBar.Value = progressBar.Minimum; // reset progressBar min
  40            progressBar.Maximum = maximum; // set progressBar max            
  41
  42            // show primes up to maximum
  43            int count = await FindPrimes(maximum);
  44            statusLabel.Text = $"Found {count} prime(s)";
  45         }
  46
  47         // displays prime numbers in primesTextBox
  48         private async Task<int> FindPrimes(int maximum)
  49         {
  50            var primeCount = 0;
  51
  52            // find primes less than maximum
  53            for (var i = 2; i < maximum && !Canceled; ++i)
  54            {
  55                // if i is prime, display it
  56                if (await Task.Run(() => IsPrime(i)))
  57                {
  58                   ++primeCount; // increment number of primes found
  59                   primesTextBox.AppendText($"{i}{Environment.NewLine}");
  60                }
  61
  62                var percentage = (double)progressBar.Value /
  63                   (progressBar.Maximum - progressBar.Minimum + 1);
  64                percentageLabel.Text = $"{percentage:P0}";
  65                progressBar.Value = i + 1; // update progress
  66            }
  67
  68            // display message if operation was canceled
  69            if (Canceled)
  70            {
  71               primesTextBox.AppendText($"Canceled{Environment.NewLine}");
  72            }
  73
  74            getPrimesButton.Enabled = true; // enable getPrimesButton
  75            cancelButton.Enabled = false; // disable cancelButton
  76            return primeCount;
  77         }
  78
  79         // check whether value is a prime number
  80         // and mark all multiples as not prime
  81         public bool IsPrime(int value)
  82         {
  83            // if value is prime, mark all of multiples
  84            // as not prime and return true
  85            if (primes[value])
  86            {
  87               // mark all multiples of value as not prime
  88               for (var i = value + value; i < primes.Length; i += value)
  89               {
  90                  primes[i] = false; // i is not prime
  91               }
  92
  93               return true;
  94            }
  95            else
  96            {
  97               return false;
  98            }
  99         }
  100
  101        // if user clicks Cancel Button, stop displaying primes
  102        private void cancelButton_Click(object sender, EventArgs e)
  103        {
  104           Canceled = true;
  105           getPrimesButton.Enabled = true; // enable getPrimesButton
  106           cancelButton.Enabled = false; // disable cancelButton
  107        }
  108      }
  109   }

Sieve of Eratosthenes

Line 14 declares the bool array primes, which we use with the Sieve of Eratosthenes algorithm (described in Exercise 8.27) to find all prime numbers less than a maximum value. The Sieve of Eratosthenes takes a list of integers and, beginning with the first prime, filters out all multiples of that prime. It then moves to the next number not yet filtered out, which is the next prime, then eliminates all of its multiples. It continues until all nonprimes have been filtered out. Algorithmically, we begin with element 2 of the bool array (ignoring elements 0 and 1) and set the elements at all indices that are multiples of 2 to false to indicate that they’re divisible by 2 and thus not prime. We then move to the next array element, check whether it’s true, and if so set all of its multiples to false to indicate that they’re divisible by the current index. When the algorithm completes, all indices that contain true are prime, as they have no divisors. The Sieve of Eratosthenes in this example is implemented by methods FindPrimes (lines 48–77) and IsPrime (lines 81–99). Each time IsPrime determines that a specific number is prime, it immediately eliminates all multiples of that number. As implemented, the algorithm in this example is intentionally inefficient. Exercise 23.10 discusses why and asks you to modify the program.

Constructor

Class FindPrimesForm’s constructor (lines 16–21) sets progressBar’s Minimum property to 2—the first prime number—and sets the percentageLabel’s Text to 0 formatted as a whole-number percentage. In the format specifier P0, P indicates that the value should be formatted as a percentage and 0 indicates zero decimal places.

async Method getPrimesButton_Click

When the user enters a number in the maxValueTextBox and presses the Get Primes Button, method getPrimesButton_Click (lines 24–45) is called. This method is declared async because it will await the results of the FindPrimes method. Line 27 gets the maximum value entered by the user, then line 30 creates a bool array with that number of elements and fills it with true values. The elements with indices that are not prime numbers will eventually be set to false. Enumerable static method Repeat creates a list of elements containing its first argument’s value. The second argument specifies the length of the list. We then call ToArray on the result to get an array representation of the elements. Repeat is a generic method—the type of the list it returns is determined by the first argument’s type.

Lines 33–40 reset the Canceled property to false and reset the GUI to prepare to determine the prime numbers. Lines 39–40 reset the progressBar’s Value to the Minimum value and set Maximum to the new value entered by the user. As we test each number from 2 to the maximum to determine whether its prime, we’ll set the progressBar’s Value property to the current number being tested. As this number increases, the progressBar will fill proportionally with color to show the asynchronous task’s progress.

Line 43 calls async method FindPrimes to begin the process of finding prime numbers. Upon completion, FindPrimes returns the number of primes less than the maximum value entered by the user, which the app then displays at line 44.

async Method FindPrimes

The async method FindPrimes implements the Sieve of Eratosthenes algorithm, displays the primes that are found and updates the progressBar and percentage completion. Line 50 initially sets primeCount to 0 to indicate that no primes have been found yet. Lines 53–66 iterate through the values from 2 up to, but not including, the maximum entered by the user. For each value, line 56 launches an async Task to determine whether that value is prime and awaits the Task’s result. When that result is returned, if it’s true, line 58 increments primeCount to indicate that a prime number was found and line 59 appends that number’s string representation to the primesTextBox’s text—thus displaying one of the intermediate results. Regardless of whether a value is prime, lines 62–64 calculate the percentage of the loop that has completed so far and display that percentage, and line 65 updates the progressBar’s Value.

At any point during the execution of FindPrimes, the user could click the app’s Cancel Button, in which case property Canceled will be set to true and the loop will terminate early. If this occurs, lines 69–72 display "Canceled" in the primesTextBox.

Method IsPrime

Method IsPrime (lines 81–99) is called by async method FindPrimes to perform part of the Sieve of Eratosthenes. IsPrime tests whether its value argument is prime by checking the corresponding element in array primes (line 85). If value is prime, lines 88–91 set to false the primes elements at all indices that are multiples of value, then line 93 returns true to indicate that value is prime; otherwise, the method returns false.

Method cancelButton_Click

When the user clicks the Cancel Button, method cancelButton_Click (lines 102–107) sets property Canceled to true, then enables the Get Primes Button and disables the Cancel Button. When the condition at line 53 is evaluated next, the loop in method FindPrimes terminates.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset