Here is the code snippet taken from Mara Bos's book:
let increment = Mutex::new(0);
thread::scope(|s|
{
for _ in 0..10
{
s.spawn(||
{
let mut gurad = increment.lock().unwrap();
for _ in 0..100
{
*gurad += 1;
}
drop(gurad);
thread::sleep(Duration::from_secs(1));
});
}
});
This code snippet takes only about one second to execute. Because all the ten threads in this code snippet can execute their one second sleep at the same time. How?
My reasoning 01:
For loop spawned 10 threads. Any of the threads acquired the lock of Mutex, increments the guard 100 times, drops the guard, and sleeps for one second. So 10 threads take 10 seconds in total to execute this code snippet.
This reasoning clearly incorrect as this code snippet takes only a second to execute.
My reasoning 02:
For loop spawned 10 threads. Any of the threads acquires the lock of Mutex, increments the guard 100 times(while all other threads wait for the lock), and drops the guard, the rest 9 threads acquire the lock one by one hence increment the guard, when all the 10 threads are done with incrementing they all sleep at the same time for one second so it takes only one second for 10 threads to sleep for one second each.
But something bothers me to understand the reasoning 02. The thread that acquired the lock first does not finish its execution after dropping the guard. This seems true for the rest 9 threads too, when all of them are done with incrementing and dropping the guard, they all sleep for a second at the same time. How can it be that a thread is still in a live state after dropping the guard manually to sleep at a later point together with the rest of the threads?
This is incorrect. When one thread drops the guard it can already start sleeping while the next one gets the lock. This means they don't start sleeping at the exact same time, nor that they will stop sleeping at the same time.
The reason it seemingly takes only one second is because incrementing the guard 100 times is extremely quick, so you don't (or can't!) even notice. Do you think you can notice a 0.0001 seconds difference in a task that takes ~1 second?
We drop the guard manually even before the thread executes the thread::sleep() function. We do not have to wait for the thread to finish its work so that the guard/lock gets unlocked to be locked by another thread.
So before a thread finishes its work fully(executing sleeping statement) another thread gets the lock/guard to start working which saves time.
Don't you think that the above-mentioned point has anything to do with the code snippet which only takes a second?
Here is another code snippet that takes exactly ten seconds to execute. It is slightly modifies version of question's code, we do not drop the guard manually here.
let increment = Mutex::new(0);
thread::scope(|s|
{
for _ in 0..10
{
s.spawn(||
{
let mut gurad = increment.lock().unwrap();
for _ in 0..100
{
*gurad += 1;
}
thread::sleep(Duration::from_secs(1));
});
}
});
So we wait for the thread to finish its work completely, after which the lock/guard gets unlocked to be used by another thread.
No, why would you conclude that? The whole point of threads is that they run in parallel. The threads first drop the lock guard (allowing other threads to continue), and only then do they sleep for a second.
It takes over one second. It will never take less on a correctly working system.
It is the counter protected by the guard that is incremented.
This is key to understanding that others haven't pointed out.
It is CPUs (processors) that execute. An operating system adds threads and has a scheduler.
A thread can be active (running on a processor) or idle.
Acquiring a lock is something your program tells the OS to do. If the lock can't be acquired (due to another holding it) then the OS makes that thread idle (until guard is dropped.)
Sleep is another action your program tells the OS to do. Thread is idle during sleep.