-1

I have been reading over the following tutorial: C++ Multithreading Tutorial. I have compiled the code in the tutorial that creates ten unique threads and print a string with the thread number.

Here is what the code looks like for those who don't want to open the link:

#include <iostream>
#include <thread>

static const int num_threads = 10;

//This function will be called from a thread

void call_from_thread(int tid) {
    std::cout << "Launched by thread " << tid << std::endl;
}

int main() {
    std::thread t[num_threads];

    //Launch a group of threads
    for (int i = 0; i < num_threads; ++i) {
        t[i] = std::thread(call_from_thread, i);
    }

    std::cout << "Launched from the main\n";

    //Join the threads with the main thread
    for (int i = 0; i < num_threads; ++i) {
        t[i].join();
    }

    return 0;
}

When I run the code it compiles and the output is kind of random. It will launch each thread but it won't launch them in order.

I was reading the C++ reference on std::mutex and it sounds like that is what I need.

So, I was wondering if someone could give me a quick rundown over how to implement std:mutex in code like this to ensure that the threads don't use the same shared resource and to ensure that they launch in order.

Change
  • 11
  • 3

1 Answers1

1

The threads are created in the right order, but the order of scheduling of their execution is not guaranteed to be the same.

Is mutex the solution ?

You could attempt to add a mutex. This will only assure that two threads are not in the critical section at the same time:

std::mutex mtx; 

void call_from_thread(int tid) {
    std::lock_guard<std::mutex> lock(mtx);  // holds the lock until scope is left
    std::cout << "Launched by thread " << tid << std::endl;
}

Note tha I didn't lock the mutex directly and I prefered the lock_guard: this locks the mutex using RAII which is exception safe.

Online demo 1

Is atomic the solution

Another way to do multithreading without mutex, is to use atomic variables. These are guaranteed to be accessed by one thread at a time without data race.

std::atomic<int> cnt{0}; 

void call_from_thread(int tid) {
    while (cnt!=tid) 
        std::this_thread::yield();  
    std::cout << "Launched by thread " << tid << std::endl;
    cnt++; 
}

Of course the code above is useless: it just makes sure that threads are executed in sequence. Every thread looks it the global atomic counter corresponds to its number. if yes, it executes and increments the global counter. If not, it just gives the opportunity to another thread to execute.

Online demo 2

Of course this construct here is a waste of time. Normally, you'd use condition variables to do something like this. it's only for illustration.

Conclusion

Multithreading is quite complex. If you want to dig into it, I highly recommend Anthony William's book "C++ Concurrency in action", which is an excellent step by step introduction, not only to C++ multithreading libraries, but more generally challenges of multithreaded algorithms.

Christophe
  • 68,716
  • 7
  • 72
  • 138