Lab10

Lab 10: Parallel and Asynchronous Programming #

Starter Code #

Student

Generating Fractals #

What is parallel programming?

Parallel programming is a programming paradigm that utilizes the architecture of modern, multi-core processors (CPUs) to perform multiple computations simultaneously.

Instead of processing data sequentially in a single thread, we divide the work among multiple threads that execute their tasks at the same time.

Task Description #

The goal of the task is to implement and compare the execution time of different methods for parallelizing the computations performed while generating the Mandelbrot Set.

The starter code contains an abstract class MandelbrotSetGenerator that manages the entire process of generating the fractal and saving it to a .png file.

The SingleThreadGenerator class is a concrete single-threaded implementation of the generator. It uses a simple, nested for loop to iterate over all pixels of the generated image. It will serve as a baseline for performance measurement.

You must implement the following computation parallelization methods:

  • MultiThreadGenerator: A multi-threaded method that manually creates and manages Thread objects.
  • TasksGenerator: A method that uses the Task class from the Task Parallel Library (TPL) to manage parallel work on the ThreadPool.
  • ParallelGenerator: A method that uses the high-level Parallel class from the TPL.

Program.cs contains the logic for measuring time and running each generator sequentially.

A correctly generated fractal should look as follows:

mandelbrotset.png

Implementation Notes

  • Number of threads:
    • In the MultiThreadGenerator and TasksGenerator implementations, you should create N units of work (threads/tasks), where N is equal to the number of processor cores. This is the optimal number for 100% CPU-bound tasks.
  • Work division:
    • When generating the fractal, the simplest strategy is to divide the image into N equal, horizontal strips.
    • Calculate how many rows fall to one thread, and then in a loop, pass the appropriate range to each thread/task for processing.

Helpful Materials:

Example Solution #

Solution

Offer Aggregator #

What is asynchronous programming?

Asynchronous programming is used to avoid blocking the execution thread while waiting for operations that are outside our control. We use it for tasks such as waiting for a response from a network (API), sending queries to a database, or reading the contents of a large file from disk.

In this case, the goal is not faster computation, but better management of waiting time and application responsiveness.

Task Description #

The goal of the task is to build an asynchronous console client (in the FlightScanner.Client project) that will aggregate flight offers from multiple services simultaneously. The client will communicate with a locally running REST API (the FlightScanner.API project).

The API, running at http://localhost:5222 (the address can be found in the launchSettings.json file), is fully implemented and does not require modification. It must be run before running the console application.

On Linux run the FlightScanner.API project through the provided script run.sh. It will download missing ASP.NET runtime, then run the project.

The client application should:

  • Query the “central” API endpoint (/api/providers) once to get the list of available airlines.
  • For each airline from the retrieved list, the application must simultaneously (concurrently) send a request to its dedicated endpoint (e.g., /api/flights/reliable-air) to fetch specific flight offers.
  • All fetched offers from different airlines must be collected, filtered for errors, and aggregated into a single, “flat” list.
  • Finally, the application must display the 10 cheapest offers found.

Data Models #

The API operates on the following DTOs (Data Transfer Objects). They are located in the FlightScanner.Common shared class library.

// Received from /api/providers
public record PartnerAirlineDto(
    string Id,
    string Name,
    string Endpoint
);

// Received from airline providers
public record ProviderResponseDto(
    string ProviderName,
    List<FlightOfferDto> Flights
);

// Part of ProviderResponseDto
public record FlightOfferDto(
    string FlightId,
    string Origin,
    string Destination,
    decimal Price
);

Implementation Notes

  • Concurrency:
    • Querying all airlines must happen at the same time, not sequentially.
  • Global timeout:
    • The entire offer collection operation must have a global time limit (e.g., 3 seconds), imposed by a CancellationTokenSource.
    • If any provider does not respond within this time, its offer is skipped.
  • Progress Reporting:
    • The application must report progress in real-time using the IProgress<T> interface.
  • Fault Tolerance:
    • If a provider’s endpoint returns an HTTP error (e.g., 500, 404) or exceeds the time limit, the application must not stop.
    • It should ignore that provider and continue working with the others.

Helpful Materials:

Example Solution #

Solution

Output:

comments powered by Disqus