admin管理员组文章数量:1026955
I'm trying to understand c++ semaphore and mutex, and I found out that I'm locking 1 mutex several times, or at least my debugging messages are showing that is the case. Even though we have only 1 mutex in semaphore, I'm aquiring it for more than 1 thread. I understand that semaphore is supposed to allow the access by mutltiple threads (limited by the counter), but internally semaphore is made of mutex that can have value 0 or 1, taken or not taken. In my opinion it shall be not possible that 2 threads will lock the mutex 1 after another, but I can see that in the log file
/**
* Connecting cell phones to a charger
*/
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <cstdio>
#include <bits/stdc++.h>
unsigned int data_race = 0;
class Semaphore {
public:
Semaphore(unsigned long init_count) {
count_ = init_count;
}
void acquire(int id) { // decrement the internal counter
printf("%d wants to aquire.\n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d aquired mutex in aquire\n", id);
while (!count_) {
printf("%d is waiting for lock\n", id);
cv_.wait(lck);
}
printf("%d is about to decrease count\n", id);
count_--;
}
void release(int id) { // increment the internal counter
printf("%d is about to relase \n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d acuired mutex in release\n", id);
count_++;
lck.unlock();
cv_.notify_one();
}
private:
std::mutex m_;
std::condition_variable cv_;
unsigned long count_;
};
Semaphore charger(2);
void cell_phone(int id) {
charger.acquire(id);
printf("Phone %d is charging...\n", id);
srand(id); // charge for "random" amount between 1-3 seconds
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 2000 + 1000));
printf("Phone %d is DONE charging!\n", id);
charger.release(id);
}
int main() {
std::thread phones[6];
for (int i=0; i<6; i++) {
phones[i] = std::thread(cell_phone, i);
}
for (auto& p : phones) {
p.join();
}
}
It's compiled with c++17, I thought that only 1 mutex will be aquired at once in this implemenation.
The result:
PS C:\HHardDrive\embedded C\output> & .\'Semaphore_CriticalSection2.exe'
0 wants to aquire.
0 aquired mutex in aquire //->Mutex locked first time
0 is about to decrease count
Phone 0 is charging...
4 wants to aquire.
4 aquired mutex in aquire //->Mutex locked second time
4 is about to decrease count
Phone 4 is charging...
3 wants to aquire.
3 aquired mutex in aquire
3 is waiting for lock
2 wants to aquire.
2 aquired mutex in aquire
2 is waiting for lock
5 wants to aquire.
5 aquired mutex in aquire
1 wants to aquire.
5 is waiting for lock
1 aquired mutex in aquire
1 is waiting for lock
Phone 0 is DONE charging!
0 is about to relase
0 acuired mutex in release
Phone 4 is DONE charging!
3 is about to decrease count
4 is about to relase
4 acuired mutex in release
Phone 3 is charging...
2 is about to decrease count
Phone 2 is charging...
Phone 2 is DONE charging!
2 is about to relase
Phone 3 is DONE charging!
2 acuired mutex in release
3 is about to relase
5 is about to decrease count
Phone 5 is charging...
3 acuired mutex in release
1 is about to decrease count
Phone 1 is charging...
Phone 1 is DONE charging!
Phone 5 is DONE charging!
1 is about to relase
5 is about to relase
1 acuired mutex in release
5 acuired mutex in release
I'm trying to understand c++ semaphore and mutex, and I found out that I'm locking 1 mutex several times, or at least my debugging messages are showing that is the case. Even though we have only 1 mutex in semaphore, I'm aquiring it for more than 1 thread. I understand that semaphore is supposed to allow the access by mutltiple threads (limited by the counter), but internally semaphore is made of mutex that can have value 0 or 1, taken or not taken. In my opinion it shall be not possible that 2 threads will lock the mutex 1 after another, but I can see that in the log file
/**
* Connecting cell phones to a charger
*/
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <cstdio>
#include <bits/stdc++.h>
unsigned int data_race = 0;
class Semaphore {
public:
Semaphore(unsigned long init_count) {
count_ = init_count;
}
void acquire(int id) { // decrement the internal counter
printf("%d wants to aquire.\n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d aquired mutex in aquire\n", id);
while (!count_) {
printf("%d is waiting for lock\n", id);
cv_.wait(lck);
}
printf("%d is about to decrease count\n", id);
count_--;
}
void release(int id) { // increment the internal counter
printf("%d is about to relase \n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d acuired mutex in release\n", id);
count_++;
lck.unlock();
cv_.notify_one();
}
private:
std::mutex m_;
std::condition_variable cv_;
unsigned long count_;
};
Semaphore charger(2);
void cell_phone(int id) {
charger.acquire(id);
printf("Phone %d is charging...\n", id);
srand(id); // charge for "random" amount between 1-3 seconds
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 2000 + 1000));
printf("Phone %d is DONE charging!\n", id);
charger.release(id);
}
int main() {
std::thread phones[6];
for (int i=0; i<6; i++) {
phones[i] = std::thread(cell_phone, i);
}
for (auto& p : phones) {
p.join();
}
}
It's compiled with c++17, I thought that only 1 mutex will be aquired at once in this implemenation.
The result:
PS C:\HHardDrive\embedded C\output> & .\'Semaphore_CriticalSection2.exe'
0 wants to aquire.
0 aquired mutex in aquire //->Mutex locked first time
0 is about to decrease count
Phone 0 is charging...
4 wants to aquire.
4 aquired mutex in aquire //->Mutex locked second time
4 is about to decrease count
Phone 4 is charging...
3 wants to aquire.
3 aquired mutex in aquire
3 is waiting for lock
2 wants to aquire.
2 aquired mutex in aquire
2 is waiting for lock
5 wants to aquire.
5 aquired mutex in aquire
1 wants to aquire.
5 is waiting for lock
1 aquired mutex in aquire
1 is waiting for lock
Phone 0 is DONE charging!
0 is about to relase
0 acuired mutex in release
Phone 4 is DONE charging!
3 is about to decrease count
4 is about to relase
4 acuired mutex in release
Phone 3 is charging...
2 is about to decrease count
Phone 2 is charging...
Phone 2 is DONE charging!
2 is about to relase
Phone 3 is DONE charging!
2 acuired mutex in release
3 is about to relase
5 is about to decrease count
Phone 5 is charging...
3 acuired mutex in release
1 is about to decrease count
Phone 1 is charging...
Phone 1 is DONE charging!
Phone 5 is DONE charging!
1 is about to relase
5 is about to relase
1 acuired mutex in release
5 acuired mutex in release
Share
Improve this question
edited Nov 16, 2024 at 17:30
Daniel Dobiński
asked Nov 16, 2024 at 15:36
Daniel DobińskiDaniel Dobiński
32 bronze badges
15
|
Show 10 more comments
1 Answer
Reset to default 0Thread 0 unlocked the mutex in acquire()
in the destructor of std::unique_lock<std::mutex> lck
, which executed upon exiting the block where it was defined. This happened just before acquire
returned, and shortly after the message "0 is about to decrease count" was printed. So thread 0 had indeed unlocked the lock before thread 4 locked it.
This RAII pattern is the whole point of unique_lock
; it locks the mutex at the point where the unique_lock
is defined (i.e. where its constructor runs), holds the lock from there until the unique_lock
goes out of scope (normally the end of the enclosing block), and then unlocks it in the destructor.
I'm trying to understand c++ semaphore and mutex, and I found out that I'm locking 1 mutex several times, or at least my debugging messages are showing that is the case. Even though we have only 1 mutex in semaphore, I'm aquiring it for more than 1 thread. I understand that semaphore is supposed to allow the access by mutltiple threads (limited by the counter), but internally semaphore is made of mutex that can have value 0 or 1, taken or not taken. In my opinion it shall be not possible that 2 threads will lock the mutex 1 after another, but I can see that in the log file
/**
* Connecting cell phones to a charger
*/
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <cstdio>
#include <bits/stdc++.h>
unsigned int data_race = 0;
class Semaphore {
public:
Semaphore(unsigned long init_count) {
count_ = init_count;
}
void acquire(int id) { // decrement the internal counter
printf("%d wants to aquire.\n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d aquired mutex in aquire\n", id);
while (!count_) {
printf("%d is waiting for lock\n", id);
cv_.wait(lck);
}
printf("%d is about to decrease count\n", id);
count_--;
}
void release(int id) { // increment the internal counter
printf("%d is about to relase \n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d acuired mutex in release\n", id);
count_++;
lck.unlock();
cv_.notify_one();
}
private:
std::mutex m_;
std::condition_variable cv_;
unsigned long count_;
};
Semaphore charger(2);
void cell_phone(int id) {
charger.acquire(id);
printf("Phone %d is charging...\n", id);
srand(id); // charge for "random" amount between 1-3 seconds
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 2000 + 1000));
printf("Phone %d is DONE charging!\n", id);
charger.release(id);
}
int main() {
std::thread phones[6];
for (int i=0; i<6; i++) {
phones[i] = std::thread(cell_phone, i);
}
for (auto& p : phones) {
p.join();
}
}
It's compiled with c++17, I thought that only 1 mutex will be aquired at once in this implemenation.
The result:
PS C:\HHardDrive\embedded C\output> & .\'Semaphore_CriticalSection2.exe'
0 wants to aquire.
0 aquired mutex in aquire //->Mutex locked first time
0 is about to decrease count
Phone 0 is charging...
4 wants to aquire.
4 aquired mutex in aquire //->Mutex locked second time
4 is about to decrease count
Phone 4 is charging...
3 wants to aquire.
3 aquired mutex in aquire
3 is waiting for lock
2 wants to aquire.
2 aquired mutex in aquire
2 is waiting for lock
5 wants to aquire.
5 aquired mutex in aquire
1 wants to aquire.
5 is waiting for lock
1 aquired mutex in aquire
1 is waiting for lock
Phone 0 is DONE charging!
0 is about to relase
0 acuired mutex in release
Phone 4 is DONE charging!
3 is about to decrease count
4 is about to relase
4 acuired mutex in release
Phone 3 is charging...
2 is about to decrease count
Phone 2 is charging...
Phone 2 is DONE charging!
2 is about to relase
Phone 3 is DONE charging!
2 acuired mutex in release
3 is about to relase
5 is about to decrease count
Phone 5 is charging...
3 acuired mutex in release
1 is about to decrease count
Phone 1 is charging...
Phone 1 is DONE charging!
Phone 5 is DONE charging!
1 is about to relase
5 is about to relase
1 acuired mutex in release
5 acuired mutex in release
I'm trying to understand c++ semaphore and mutex, and I found out that I'm locking 1 mutex several times, or at least my debugging messages are showing that is the case. Even though we have only 1 mutex in semaphore, I'm aquiring it for more than 1 thread. I understand that semaphore is supposed to allow the access by mutltiple threads (limited by the counter), but internally semaphore is made of mutex that can have value 0 or 1, taken or not taken. In my opinion it shall be not possible that 2 threads will lock the mutex 1 after another, but I can see that in the log file
/**
* Connecting cell phones to a charger
*/
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <cstdio>
#include <bits/stdc++.h>
unsigned int data_race = 0;
class Semaphore {
public:
Semaphore(unsigned long init_count) {
count_ = init_count;
}
void acquire(int id) { // decrement the internal counter
printf("%d wants to aquire.\n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d aquired mutex in aquire\n", id);
while (!count_) {
printf("%d is waiting for lock\n", id);
cv_.wait(lck);
}
printf("%d is about to decrease count\n", id);
count_--;
}
void release(int id) { // increment the internal counter
printf("%d is about to relase \n", id);
std::unique_lock<std::mutex> lck(m_);
printf("%d acuired mutex in release\n", id);
count_++;
lck.unlock();
cv_.notify_one();
}
private:
std::mutex m_;
std::condition_variable cv_;
unsigned long count_;
};
Semaphore charger(2);
void cell_phone(int id) {
charger.acquire(id);
printf("Phone %d is charging...\n", id);
srand(id); // charge for "random" amount between 1-3 seconds
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 2000 + 1000));
printf("Phone %d is DONE charging!\n", id);
charger.release(id);
}
int main() {
std::thread phones[6];
for (int i=0; i<6; i++) {
phones[i] = std::thread(cell_phone, i);
}
for (auto& p : phones) {
p.join();
}
}
It's compiled with c++17, I thought that only 1 mutex will be aquired at once in this implemenation.
The result:
PS C:\HHardDrive\embedded C\output> & .\'Semaphore_CriticalSection2.exe'
0 wants to aquire.
0 aquired mutex in aquire //->Mutex locked first time
0 is about to decrease count
Phone 0 is charging...
4 wants to aquire.
4 aquired mutex in aquire //->Mutex locked second time
4 is about to decrease count
Phone 4 is charging...
3 wants to aquire.
3 aquired mutex in aquire
3 is waiting for lock
2 wants to aquire.
2 aquired mutex in aquire
2 is waiting for lock
5 wants to aquire.
5 aquired mutex in aquire
1 wants to aquire.
5 is waiting for lock
1 aquired mutex in aquire
1 is waiting for lock
Phone 0 is DONE charging!
0 is about to relase
0 acuired mutex in release
Phone 4 is DONE charging!
3 is about to decrease count
4 is about to relase
4 acuired mutex in release
Phone 3 is charging...
2 is about to decrease count
Phone 2 is charging...
Phone 2 is DONE charging!
2 is about to relase
Phone 3 is DONE charging!
2 acuired mutex in release
3 is about to relase
5 is about to decrease count
Phone 5 is charging...
3 acuired mutex in release
1 is about to decrease count
Phone 1 is charging...
Phone 1 is DONE charging!
Phone 5 is DONE charging!
1 is about to relase
5 is about to relase
1 acuired mutex in release
5 acuired mutex in release
Share
Improve this question
edited Nov 16, 2024 at 17:30
Daniel Dobiński
asked Nov 16, 2024 at 15:36
Daniel DobińskiDaniel Dobiński
32 bronze badges
15
- 1 When you wait on a condition variable the mutex is released during the time the thread sleeps (and is re-acquired for checking the condition). – wohlstad Commented Nov 16, 2024 at 15:39
- 4 A side note: Why should I not #include <bits/stdc++.h>?. – wohlstad Commented Nov 16, 2024 at 15:39
- 2 Also : NEVER wait on a condition variable without a predicate! Condition variables can have what is called "spurious wakeups" (something C++ cannot help, it is an OS thing). S – Pepijn Kramer Commented Nov 16, 2024 at 16:27
-
2
@PepijnKramer never say never. calling
wait
from a while loop (as they do here) is exactly equivalent to using a predicate – Alan Birtles Commented Nov 16, 2024 at 16:33 - 1 Two hints: First, if you want to discuss about locking mutexes only, please make your example more simple, without srand, just two concurrent threads, clear timings etc. Second, add timestamps to your debug messages, because you shouldn't rely on the sequence of stdout, only. – Markus Commented Nov 16, 2024 at 18:00
1 Answer
Reset to default 0Thread 0 unlocked the mutex in acquire()
in the destructor of std::unique_lock<std::mutex> lck
, which executed upon exiting the block where it was defined. This happened just before acquire
returned, and shortly after the message "0 is about to decrease count" was printed. So thread 0 had indeed unlocked the lock before thread 4 locked it.
This RAII pattern is the whole point of unique_lock
; it locks the mutex at the point where the unique_lock
is defined (i.e. where its constructor runs), holds the lock from there until the unique_lock
goes out of scope (normally the end of the enclosing block), and then unlocks it in the destructor.
本文标签:
版权声明:本文标题:c++ - How is it possible that I'm locking a mutex multiple times, if mutex shall be possible to be locked only once? - S 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745654362a2161509.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
wait
from a while loop (as they do here) is exactly equivalent to using a predicate – Alan Birtles Commented Nov 16, 2024 at 16:33