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.
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.
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 await
s 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
.
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
.
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.