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
?
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With