Laboratory Task 2

by Paweł Paduch published 2024/11/14 13:56:43 GMT+1, last modified 2024-11-14T13:56:43+01:00
More advanced topics in task creation and management.
  1. ExecuteSynchronously (0.5 pts)
    • Create a new project named LabTask1 in a new solution.
    • Create the first task (initially empty) using Task task = Task.Factory.StartNew(() => {});
    • Then, in a loop, call the task continuation a million times to execute one after another.
      Task t = task.ContinueWith((ant, a) => {}, i
      //,TaskContinuationOptions.ExecuteSynchronously 
      ); 
      task = t;
    • Measure the initialization time (after the loop creating a million tasks) using Stopwatch. Note that time measurements should be performed without the Debug option, i.e., by pressing Ctrl+F5.
    • Wait for the task to complete and measure the time again.
    • Uncomment TaskContinuationOptions.ExecuteSynchronously
    • Perform new measurements. You should perform 5 measurements and calculate the average.

  2. ExecuteSynchronously with heavier tasks (0.5 pts)
    • Add a function that will heat up the processor a bit, to be run in each task:
    • static void DoPointlessMess() 
      { 
          double aa = 0; 
          for (int i = 0; i < 100; i++) 
          { 
              aa++; 
              aa = (Math.Sin(aa) + Math.Cos(aa)); 
              aa = aa % Math.Sin(aa); 
              aa = Math.Log2(Math.Tanh(aa)); 
              aa *= Math.Sin(aa) / Math.Cos(aa) == 0 ? 1 : Math.Cos(aa); 
          } 
      }
    • Perform the above measurements with and without TaskContinuationOptions.ExecuteSynchronously. Did anything change?

  3. ExecuteSynchronously with value passing (0.5 pts)
    • ContinueWith is often used to pass data from one task to another.
      Modify the function DoPointlessMess to return the result of calculations, and the variable aa should be passed from outside.
    • Modify the invocation of Task.Factory.StartNew to include data passing to and from the function DoPointlessMess.
    • Perform tests as in the previous exercise.

  4. Fibonacci Sequence (1 pt.)
    • Create a new project. Copy and modify the previous project to calculate the Fibonacci sequence.
    • Use the BigInt type for calculations of very large numbers.
    • Pass pairs of numbers using a tuple (Tuple<BigInt, BigInt>).
    • You can test the functionality by creating 200,000 tasks.
      static Tuple<BigInteger, BigInteger> BigFibonacci(BigInteger n2, BigInteger n1, int iteration) 
      { 
          // Console.WriteLine($"{n2}"); 
          return Tuple.Create(n1, n1 + n2); 
      }
    • Out of curiosity, you can look at the generated numbers, or just the final result (note that the final number occupies several screens :) ).
    • For the curious, compare the execution time with a single-threaded solution:
      BigInteger n2 = 0; 
      BigInteger n1 = 1; 
      for (int i = 0; i <= 200000; i++) 
      { 
          Tuple<BigInteger, BigInteger> result = BigFibonacci(n2, n1, n); 
          n1 = result.Item2; 
          n2 = result.Item1; 
      }
  5. Prime Numbers (1.5 pts)
    • Create a new project based on the previous one.
    • Use the following function to calculate prime numbers in a range
      int countFromTotal = 0;
      int countToTotal = 100000000;
      public static bool IsPrime(int liczba) 
      { 
        if (liczba <= 1) return false; // Numbers less than or equal to 1 are not prime 
        if (liczba <= 3) return true; // 2 and 3 are prime numbers 
      
        // Check divisibility by 2 and 3 
        if (liczba % 2 == 0 || liczba % 3 == 0) return false;
      
        // Check divisibility by numbers of the form 6k ± 1, where k = 1, 2, 3, ... 
        for (int i = 5; i * i <= liczba; i += 6) 
        { 
          if (liczba % i == 0 || liczba % (i + 2) == 0) 
          return false; 
        } 
        return true; 
      }
    • Save the numbers to a list List<int> singleThreadPrimes = new List<int>();
    • Measure the execution time for a single thread.
    • Then, modify the current project to calculate prime numbers based on tasks.
      • Each task receives a start and end number of the range it has to check.
      • Each task returns a list of prime numbers.
      • The number of tasks to be run should be read using Environment.ProcessorCount.
      • Create a final task using Task.Factory.ContinueWhenAll that collects data from the other tasks.
      • This task should combine the collected lists of integers into a single list and return it.
      • Measure the time taken to find all the numbers in the given range for the multi-threaded version.

  6. Create a static class PrimesGenerator (1 pt)
    • Create an asynchronous method that returns a task returning a list of ints
      public static async Task<List<int>> FindPrimesAsync(int start, int end)
    • In this method, as in the previous task, we should split the main task into smaller sub-tasks, wait for the results, and return the merged task. LINQ expression can be used.
      List<int>[] results = await Task.WhenAll(tasks);
      return results.SelectMany(primes => primes).ToList();
    • Use in the program. Measure the execution time.