我试图理解 C++ 信号量和互斥体,我发现我多次锁定 1 个互斥体,或者至少我的调试消息显示情况是这样。 尽管我们在信号量中只有 1 个互斥体,但我正在为超过 1 个线程获取它。 我知道信号量应该允许多线程访问(受计数器限制),但内部信号量由互斥体组成,互斥体的值可以为 0 或 1,无论是否采用。 在我看来,2 个线程不可能依次锁定互斥锁 1,但我可以在日志文件中看到这一点
/**
* 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();
}
}
它是用c++17编译的,我认为在这个实现中一次只会获取1个互斥量。
结果:
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
线程 0 在
acquire()
的析构函数中解锁了 std::unique_lock<std::mutex> lck
中的互斥锁,该析构函数在退出定义它的块时执行。 这发生在 acquire
返回之前,并且在打印消息“0 即将减少计数”之后不久。 所以线程 0 确实在线程 4 锁定锁之前解锁了锁。
这个 RAII 模式就是
unique_lock
的全部要点;它有效地从定义点开始一直持有锁,直到封闭块的末尾,然后将其解锁。