Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Mastering C++ Multithreading: A Hands-On Guide to Spawning Threads with Random Number Generation

Learn how to create and manage multiple threads in C++ with this step-by-step tutorial. We'll build a program that spawns 10 threads to search for a target random number, exploring join(), race conditions, and the nice utility.

C++ multithreading tutorial spawn threads C++ std::thread example join threads C++ random number generation C++ race conditions C++ nice utility Linux COP4600 threading assignment C++ concurrency for beginners multithreading in gaming AI thread synchronization C++ high-performance computing C++ C++11 threading pthread compile command thread pool C++ concurrent programming examples

Why Multithreading Matters in 2026

In today's world of AI apps, real-time gaming, and high-frequency trading, programs must do multiple things at once. Think about a battle royale game like Fortnite: while you're running, the game is also rendering graphics, processing network packets, and updating AI enemies. That's multithreading in action. In C++, threads let your program execute multiple paths concurrently, squeezing maximum performance from multi-core CPUs.

Understanding the Threading Exercise

This tutorial is based on a classic C++ assignment: spawn 10 threads, each searching for a random number that matches a target. The catch? Threads finish in random order due to race conditions. We'll walk through the solution step by step, explaining each concept.

What You'll Learn

  • How to create threads with std::thread
  • Using join() to wait for threads
  • Passing arguments to thread functions
  • Random number generation with rand() and srand()
  • Race conditions and the nice utility

Setting Up Your C++ Environment

You'll need a C++ compiler with pthread support. Compile with: g++ threads.cpp -o ex5.out -pthread -std=c++11. Ensure your virtual machine has at least 512 MB RAM and multiple cores to observe thread interleaving.

Step 1: Accept a Command-Line Argument

Your program must take one integer argument. Use argc and argv:

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: ./ex5.out <target>\n";
        return 1;
    }
    int target = std::atoi(argv[1]);
    // ...
}

Step 2: Write the Thread Function

The function will generate random numbers between 0 and 9999 until it finds the target. It needs an ID (0-9) and the target number.

void searchNumber(int id, int target) {
    std::srand(std::time(nullptr) + id); // seed per thread
    int random;
    do {
        random = std::rand() % 10000;
    } while (random != target);
    std::cout << "Thread " << id << " completed." << std::endl;
}

Note: Seeding with time(nullptr) + id ensures each thread gets a different sequence, making them less likely to finish together.

Step 3: Spawn 10 Threads in a Loop

Use a loop to create threads, storing them in an array or vector. Do not write 10 separate std::thread calls.

std::thread threads[10];
for (int i = 0; i < 10; ++i) {
    threads[i] = std::thread(searchNumber, i, target);
}

Step 4: Join All Threads

Call join() on each thread to ensure main waits for all to finish before printing the final message.

for (int i = 0; i < 10; ++i) {
    threads[i].join();
}
std::cout << "All threads have finished finding numbers." << std::endl;

Understanding Race Conditions and the nice Utility

By default, threads may finish in order if the CPU is fast. To force random order, lower your process priority using nice. Run: nice -n 19 ./ex5.out 1414. This gives your process the lowest priority, making the OS interrupt it more often, causing threads to complete at different times. This simulates real-world race conditions where thread scheduling is unpredictable.

Why This Matters

In AI training pipelines, data processing threads must be synchronized. In gaming, thread timing affects frame rates. Understanding race conditions helps you write robust concurrent code.

Full Example Code

#include <iostream>
#include <thread>
#include <cstdlib>
#include <ctime>

void searchNumber(int id, int target) {
    std::srand(std::time(nullptr) + id);
    int random;
    do {
        random = std::rand() % 10000;
    } while (random != target);
    std::cout << "Thread " << id << " completed." << std::endl;
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: ./ex5.out <target>\n";
        return 1;
    }
    int target = std::atoi(argv[1]);
    std::thread threads[10];
    for (int i = 0; i < 10; ++i) {
        threads[i] = std::thread(searchNumber, i, target);
    }
    for (int i = 0; i < 10; ++i) {
        threads[i].join();
    }
    std::cout << "All threads have finished finding numbers." << std::endl;
    return 0;
}

Testing Your Program

Run twice with nice -n 19 ./ex5.out 1414. Observe that thread completion order changes each run. Screenshot both outputs for submission.

Common Pitfalls

  • Forgetting to join threads can cause program to exit before threads finish.
  • Using same seed for all threads may cause them to finish together.
  • Not compiling with -pthread will cause linking errors.

Beyond the Assignment: Real-World Threading

Modern C++ offers std::async, std::future, and thread pools. In 2026, with the rise of AI assistants like ChatGPT, threading is crucial for handling multiple user requests concurrently. Even in mobile apps, background threads handle network calls without freezing the UI.

Trend Connection: AI Chatbots

When you ask an AI chatbot a question, your request is handled by a thread in a server pool. Each thread processes your query independently, similar to our threads searching for a random number. The server must manage hundreds of threads concurrently, ensuring fair scheduling and avoiding deadlocks.

Conclusion

You've built a multithreaded C++ program that spawns 10 threads, each searching for a target random number. You've learned about thread creation, joining, race conditions, and the nice utility. This foundation prepares you for more advanced topics like mutexes, condition variables, and thread pools. Keep experimenting with different thread counts and priorities to see how concurrency affects performance.

"Threads allow your program to do more than one thing at a time, but with great power comes great responsibility—always join your threads!"