Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order of threads in execution

Consider this simple concurrence example :

#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex

std::mutex mtx;           // mutex for critical section

void print_block(int n, char c) {
    // critical section (exclusive access to std::cout signaled by locking mtx):
    mtx.lock();
    for (int i = 0; i<n; ++i) { std::cout << c; }
    std::cout << '\n';
    mtx.unlock();
}

int main()
{
    std::thread th1(print_block, 50, '*');
    std::thread th2(print_block, 50, '$');

    th1.join();
    th2.join();

    return 0;
} 

Is it always guaranteed that th1 will be the first thread to execute the for loop ?

Meaning , when I do this :

th1.join();
th2.join();

Then can I be absolutely sure that th1 will be executed first and then th2 ?

like image 931
JAN Avatar asked Oct 04 '15 13:10

JAN


1 Answers

No, you're most likely seeing th1 always start first because thread construction for that variable is done first (and thread construction is expensive), thus th2 starts after. This doesn't mean that there's an order.

Calling join() does not have anything to do with which thread gets executed first, that's done at construction when you provide a callable.

th1 can be constructed and then stalled by the OS, which would then cause th2 to run first. There is no order unless you implement one.

Consider this example that gives a much fairer start to both threads, it sometimes outputs thread 1 as being the first to acquire the lock, it sometimes outputs thread 2.

Example:

#include <iostream>         // std::cout
#include <string>           // std::string
#include <unordered_map>    // std::unordered_map<K, V>
#include <thread>           // std::thread
#include <mutex>            // std::mutex
#include <atomic>           // std::atomic<T>

std::unordered_map<std::thread::id, std::string> thread_map;
std::mutex mtx;           // mutex for critical section
std::atomic<bool> go{ false };

void print_block( int n, char c )
{
    while ( !go ) {} // prevent threads from executing until go is set.
    // critical section (exclusive access to std::cout signaled by locking mtx):
    mtx.lock();

    std::cout << thread_map.find( std::this_thread::get_id() )->second <<
        " acquires the lock.\n";

    mtx.unlock();
}

int main()
{
    std::thread th1( print_block, 50, '*' );
    std::thread th2( print_block, 50, '$' );

    thread_map.emplace( std::make_pair( th1.get_id(), "Thread 1" ) );
    thread_map.emplace( std::make_pair( th2.get_id(), "Thread 2" ) );

    go.store( true );

    th1.join();
    th2.join();

    return 0;
}
like image 61
bku_drytt Avatar answered Nov 06 '22 10:11

bku_drytt