I have two threads, both with a loop that does some work. The only real behavioral difference between the two is that Thread 1 does a sleep_for
prior to locking its mutex in the loop, Thread 2 does not. The goal is to keep Thread 1 light on the system: I do not want it hammering the CPU 100%. Thread 2 is temporary in nature, so it shouldn't sleep as it's working. But the amount of work Thread 2 does (per iteration) is heavy (hence the sleep_for
in my example after the lock, to simulate a long-held lock due to real work).
Thread 2 gets all the execution time. In fact, Thread 1 only gets 1 iteration in after 4 seconds. What I expected was for Thread 1 to lock the std::mutex
, wait for the unlock, and then it acquires the lock. Does the std::mutex
not act as a sort-of queue? Meaning, even though Thread 2 doesn't sleep before acquiring the lock, when it attempts to lock on the next iteration, it ends up having to wait for the lock to be released since Thread 1 was next in line to acquire it? Even though the sleeps are in there, I expected each Thread 1 and Thread 2 to each get a turn.
Where am I wrong here? What is the explanation of this behavior? And what are and aren't the guarantees provided by the C++ standard?
#include <thread>
#include <atomic>
#include <mutex>
#include <iostream>
#include <chrono>
using namespace std::chrono_literals;
std::unique_lock<std::mutex> LockMutex()
{
static std::mutex m;
return std::unique_lock<std::mutex>{m};
}
int main()
{
std::atomic_bool running{true};
std::thread t1{[&] {
while (running)
{
std::this_thread::sleep_for(100ms);
auto lock = LockMutex();
std::cout << "Thread 1 :: Time Slice\n";
}
}};
std::thread t2{[&] {
while (running)
{
auto lock = LockMutex();
std::cout << "Thread 2 :: Time Slice\n";
std::this_thread::sleep_for(100ms);
}
}};
std::this_thread::sleep_for(4s);
running = false;
t1.join();
t2.join();
}
Output I get is:
Start
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 2 :: Time Slice
Thread 1 :: Time Slice
0
Finish
Live code sample here for testing.