Practice Project: Simple Multithreaded Application

Practice Project: Simple Multithreaded Application

Welcome! With our recent lessons, you've gained a strong foundation in creating and managing threads, as well as understanding data sharing between threads using primitive approaches. Now, it's time to put all that knowledge into practice by building a simple multithreaded application.

Objective

In this project, you will create a basic program that simulates downloading files concurrently. This will involve:

  1. Creating a Downloader Class:

    • The Downloader class represents a task that downloads a file.
    • You will use threads to execute instances of this class concurrently, simulating asynchronous downloads.
  2. Simulating File Downloads with Delays:

    • To make the downloads feel realistic, we'll introduce delays using std::this_thread::sleep_for.
    • This will also help you understand how to manage and coordinate multiple threads working over time.
  3. Managing Multiple Threads:

    • You'll learn how to start multiple download threads and ensure they complete correctly.
    • Methods such as join() and detach() will be crucial here, ensuring that the main program waits for all downloads to finish before proceeding.

Here's a preview of what you'll be working towards:

#ifndef DOWNLOADER_H
#define DOWNLOADER_H

#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <random>

class Downloader {
public:
    Downloader(const std::string& fileName) : fileName_(fileName), rndEngine_(std::random_device{}()) {}

    void operator()() {
        try {
            std::cout << "Downloading " << fileName_ << std::endl;
            std::uniform_int_distribution<int> dist(100, 200);
            std::this_thread::sleep_for(std::chrono::milliseconds(dist(rndEngine_)));
            std::cout << "Completed " << fileName_ << std::endl;
        } catch (...) {
            std::cout << "Download interrupted for " << fileName_ << std::endl;
        }
    }

private:
    std::string fileName_;
    std::mt19937 rndEngine_;
};

#endif // DOWNLOADER_H

int main() {
    std::thread t1(Downloader("file1.txt"));
    std::thread t2(Downloader("file2.txt"));
    t1.join();
    t2.join();
    std::cout << "All downloads completed" << std::endl;
    return 0;
}

Let's break down the code snippet above:

  • The Downloader class represents a task that downloads a file. It takes the file name as a parameter and simulates a download operation.
    • The std::uniform_int_distribution object dist generates random delays between 100 and 200 milliseconds.
    • The std::this_thread::sleep_for function pauses the thread for the specified duration.
      • Note, that we use std::chrono::milliseconds to specify the duration in milliseconds and the rndEngine_ object is used to generate random numbers for the delay. We seed it with std::random_device{} to ensure different sequences each time.
    • The operator() function is the entry point for the thread, where the download operation is performed.
  • In the main function:
    • We create two threads t1 and t2, each executing an instance of the Downloader class with different file names.
    • We use the join() method to wait for both threads to complete before printing "All downloads completed".
Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal