【发布时间】:2019-10-29 02:44:29
【问题描述】:
我想实现信号量类。 stackoverflow 上的用户注意到我的实现不正确。
一开始我是这样做的:
class sem_t {
int count;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
this->count++;
}
void down() {
while (this->count == 0)
std::this_thread::yield();
this->count--;
}
};
然后stackoverflow上的一个用户注意到这个实现是错误的,因为我从任何同步原语中读取和写入变量count,并且在某些时候该值可能会变得不正确,并且在编译器优化的情况下编译器可以假设变量@ 987654327@ 不能被其他线程修改。所以,我尝试将互斥锁添加到这个结构中,我已经这样做了:
class sem_t {
int count;
std::mutex mutualExclusion;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
this->mutualExclusion.lock();
this->count++;
this->mutualExclusion.unlock();
}
void down() {
this->mutualExclusion.lock();
while (this->count == 0)
std::this_thread::yield();
this->count--;
this->mutualExclusion.unlock();
}
};
但是如果在我尝试分离线程时使用这种方法,我会收到一个错误,说互斥锁在忙碌时已被破坏,导致一个线程可以持有互斥锁,然后屈服,之后线程被分离并发生错误(这是解决方案好吗?)。 然后我尝试修改这段代码,并停止了以下构造:
class sem_t {
int count;
std::mutex mutualExclusion;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
this->mutualExclusion.lock();
this->count++;
this->mutualExclusion.unlock();
}
void down() {
while (this->count == 0)
std::this_thread::yield();
this->mutualExclusion.lock();
this->count--;
this->mutualExclusion.unlock();
}
};
但我认为这个解决方案也有问题,因为它可能会导致与第一个解决方案相同的问题。
那么,正确的实现是什么? 我想说明我尝试使用 条件变量 来实现,但我正在尝试在没有 条件变量 的情况下实现信号量,如果您想建议使用 的解决方案条件变量请描述条件变量的等待方法是如何工作的。
[编辑]
我的完整代码,使用自行实现的信号量:
#include "pch.h"
#include <iostream>
#include <vector>
#include <mutex>
#include <thread>
#include <chrono>
class sem_t {
int count;
std::mutex mutualExc;
public:
sem_t(int _count = 0) : count(_count) {};
void up() {
mutualExc.lock();
this->count++;
mutualExc.unlock();
}
void down() {
mutualExc.lock();
while (this->count == 0) {
mutualExc.unlock();
std::this_thread::yield();
mutualExc.lock();
}
this->count--;
mutualExc.unlock();
}
};
#define N 5
#define THINKING 0
#define HUNGRY 1
#define EATING 2
std::mutex mx;
std::mutex coutMX;
char philosopherState[N] = { THINKING };
sem_t philosopherSemaphores[N] = { 0 };
void testSetState(short i) {
if (philosopherState[i] == HUNGRY && philosopherState[(i + 1) % N] != EATING && philosopherState[(i + N - 1) % N] != EATING) {
philosopherState[i] = EATING;
philosopherSemaphores[i].up();
}
}
void take_forks(short i) {
::mx.lock();
philosopherState[i] = HUNGRY;
testSetState(i);
::mx.unlock();
philosopherSemaphores[i].down();
}
void put_forks(short i) {
::mx.lock();
philosopherState[i] = THINKING;
testSetState((i + 1) % N);
testSetState((i + N - 1) % N);
::mx.unlock();
}
void think(short p) {
for (short i = 0; i < 5; i++) {
coutMX.lock();
std::cout << "Philosopher N" << p << " is thinking!" << std::endl;
coutMX.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void eat(short p) {
for (short i = 0; i < 5; i++) {
coutMX.lock();
std::cout << "Philosopher N" << p << " is eating!" << std::endl;
coutMX.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void philosopher(short i) {
while (1) {
think(i);
take_forks(i);
eat(i);
put_forks(i);
}
}
int main()
{
std::vector<std::thread*> threadsVector;
for (int i = 0; i < N; i++) {
threadsVector.push_back(new std::thread([i]() { philosopher(i); }));
}
std::this_thread::sleep_for(std::chrono::milliseconds(15000));
for (int i = 0; i < N; i++) {
threadsVector[i]->detach();
}
return 0;
}
正在发生的错误(仅在 Visual Studio 中以发布或调试模式运行程序时发生)
【问题讨论】:
-
在现实生活中的编程中,你想使用
condition_variable,原子递增和递减,没有全局变量,停止线程的适当机制等。如果你对C++中的多线程很认真,你必须阅读 C++ Concurrency In Action 一书。 -
@Phil1970 好的,我会尽快阅读那本书,谢谢您的建议。
-
由于餐饮哲学家问题主要是学术性的,通常会假设它永远存在。
标签: c++ synchronization semaphore