在 C++ ncurses 项目中使用互斥锁的正确方法?
A proper way to use mutex in c++ ncurses project?
我找不到写一个好的条件来解决我的问题的方法。正如您在包含的图像中看到的那样,屏幕中间有一条线,球不断地上下移动并在一段时间后消失。新球也层出不穷
我的任务是使用互斥锁,这样只有 N 个球(2 或 3)可以在线上方,其余的都在等待轮到他们。
我尝试了几个选项,这是最近的一个。这可能没有多大意义,但我暂时没有其他想法:
来自 ball.cpp 的片段:
Ball::Ball(int nr)
{
this->nr = nr;
changeDirection();
this->x = 40;
this->y = 24;
this->lastX = 0;
this->lastY = 0;
this->bounceCounter = 0;
this->isAboveTheLine = false;
}
.......
if(y < 12) {
isAboveTheLine = true;
}
else if(y >= 12) {
isAboveTheLine = false;
}
并且来自 main.cpp:
std::mutex m;
void ballFunction(int a)
{
int counter = 2;
int nr = a;
while (run && shared->balls[nr]->bounceCounter < 5)
{
usleep(50000);
shared->balls[nr]->updateBall();
if(shared->balls[nr]->isAboveTheLine == true) {
counter++;
}
else if(shared->balls[nr]->isAboveTheLine == false) {
counter--;
}
if(counter >= 3) {
m.lock();
}
else if (counter<2) {
m.unlock();
}
}
shared->balls[nr]->x = -1;
shared->balls[nr]->y = -1;
}
编辑:我添加了 int main():
int main() {
srand(time(NULL));
window = new Window();
int i = 0;
std::vector<std::thread> threads;
std::thread threadWindow(updateWindow2);
std::thread threadExit(exit);
while(run) {
window->addBall();
threads.push_back(std::thread(ballFunction, i));
i++;
sleep(1);
}
threadWindow.join();
threadExit.join();
for(int j=2; j<i+2; j++) {
threads[j].join();
}
return 0;
}
根本不起作用。我是在朝着正确的方向前进还是需要不同的方法?
如果直接回答您的问题,我会使用 condition_variable
。等待直到出现球的空闲位置(上部的球数变得少于球数限制)。当球离开 limited-ball-number 区域时通知其他线程:
std::mutex m;
std::condition_variable cv;
int counter = 2;
void ballFunction(int a)
{
int nr = a;
while (shared->balls[nr]->bounceCounter < 5)
{
usleep(50000);
bool previousIsAbove = shared->balls[nr]->isAboveTheLine;
shared->balls[nr]->updateBall();
// check if state changed
if (previousIsAbove == shared->balls[nr]->isAboveTheLine) continue;
if(previousIsAbove) // ball went down the line
{
std::unique_lock<std::mutex> locker(m);
counter++;
cv.notify_one();
}
else
{
std::unique_lock<std::mutex> locker(m);
cv.wait(locker, [&](){return !run || counter > 0;});
if (!run) return;
counter--;
}
}
shared->balls[nr]->x = -1;
shared->balls[nr]->y = -1;
}
但是代码有很多问题,我会解释最关键的。
首先你的 counter
是 ballFunction()
中的一个 non-static 局部变量。所有 ballFunction
调用者都有自己的 counter
版本,但您不想将它用作所有线程的一个共享变量。把它放在 ballFunction()
之外。你还用 2
初始化它,所以我假设你想将它用作在线上方的一些空闲位置。但是当球越过线时你增加它并检查它与'2'和'3'。做相反的事情 - 当球越过线时减少自由位置计数并检查是否超过 0
然后减少并让球进一步上升。
假设您修复了该问题,您仍然 read/write counter
没有任何关键部分处理和线程同步的值。这可能会导致数据竞争,并且是 未定义的行为(不是程序)。仅以 thread-safe 方式处理共享数据。例如,如果您使用 mutex
来同步线程(如本例),则仅访问它 "under locked mutex" 以将同时使用共享数据的线程数限制为一个。如果您决定改用 atomic
- 请仅对 CAS (compare & swap) atomic operation 进行 检查 + increment/decrement。
run
也是如此(您也可以从不同的线程访问它,至少使其成为 volatile
)。注意:如果您使用我的代码,请不要忘记在将 run
设置为 false
时通知线程(我假设您在 exit()
中这样做)。
对于每个进入上半部分的球,每次迭代都会增加线上的球数。仅在越过线的那一刻执行此操作(我通过检查球在更新其位置之前和之后的位置来做到这一点,您也可以从之前的迭代中保存它以避免 double-check)。
我没有涉及可读性、风格等问题,只涉及主要的多线程和逻辑问题。
PS:
New balls are also appearing endlessly
因为你在这里不断地生成它们:
while(run) {
window->addBall();
threads.push_back(std::thread(ballFunction, i));
i++;
sleep(1);
}
我不知道你想让你的程序做什么。
我找不到写一个好的条件来解决我的问题的方法。正如您在包含的图像中看到的那样,屏幕中间有一条线,球不断地上下移动并在一段时间后消失。新球也层出不穷
我的任务是使用互斥锁,这样只有 N 个球(2 或 3)可以在线上方,其余的都在等待轮到他们。
我尝试了几个选项,这是最近的一个。这可能没有多大意义,但我暂时没有其他想法:
来自 ball.cpp 的片段:
Ball::Ball(int nr)
{
this->nr = nr;
changeDirection();
this->x = 40;
this->y = 24;
this->lastX = 0;
this->lastY = 0;
this->bounceCounter = 0;
this->isAboveTheLine = false;
}
.......
if(y < 12) {
isAboveTheLine = true;
}
else if(y >= 12) {
isAboveTheLine = false;
}
并且来自 main.cpp:
std::mutex m;
void ballFunction(int a)
{
int counter = 2;
int nr = a;
while (run && shared->balls[nr]->bounceCounter < 5)
{
usleep(50000);
shared->balls[nr]->updateBall();
if(shared->balls[nr]->isAboveTheLine == true) {
counter++;
}
else if(shared->balls[nr]->isAboveTheLine == false) {
counter--;
}
if(counter >= 3) {
m.lock();
}
else if (counter<2) {
m.unlock();
}
}
shared->balls[nr]->x = -1;
shared->balls[nr]->y = -1;
}
编辑:我添加了 int main():
int main() {
srand(time(NULL));
window = new Window();
int i = 0;
std::vector<std::thread> threads;
std::thread threadWindow(updateWindow2);
std::thread threadExit(exit);
while(run) {
window->addBall();
threads.push_back(std::thread(ballFunction, i));
i++;
sleep(1);
}
threadWindow.join();
threadExit.join();
for(int j=2; j<i+2; j++) {
threads[j].join();
}
return 0;
}
根本不起作用。我是在朝着正确的方向前进还是需要不同的方法?
如果直接回答您的问题,我会使用 condition_variable
。等待直到出现球的空闲位置(上部的球数变得少于球数限制)。当球离开 limited-ball-number 区域时通知其他线程:
std::mutex m;
std::condition_variable cv;
int counter = 2;
void ballFunction(int a)
{
int nr = a;
while (shared->balls[nr]->bounceCounter < 5)
{
usleep(50000);
bool previousIsAbove = shared->balls[nr]->isAboveTheLine;
shared->balls[nr]->updateBall();
// check if state changed
if (previousIsAbove == shared->balls[nr]->isAboveTheLine) continue;
if(previousIsAbove) // ball went down the line
{
std::unique_lock<std::mutex> locker(m);
counter++;
cv.notify_one();
}
else
{
std::unique_lock<std::mutex> locker(m);
cv.wait(locker, [&](){return !run || counter > 0;});
if (!run) return;
counter--;
}
}
shared->balls[nr]->x = -1;
shared->balls[nr]->y = -1;
}
但是代码有很多问题,我会解释最关键的。
首先你的 counter
是 ballFunction()
中的一个 non-static 局部变量。所有 ballFunction
调用者都有自己的 counter
版本,但您不想将它用作所有线程的一个共享变量。把它放在 ballFunction()
之外。你还用 2
初始化它,所以我假设你想将它用作在线上方的一些空闲位置。但是当球越过线时你增加它并检查它与'2'和'3'。做相反的事情 - 当球越过线时减少自由位置计数并检查是否超过 0
然后减少并让球进一步上升。
假设您修复了该问题,您仍然 read/write counter
没有任何关键部分处理和线程同步的值。这可能会导致数据竞争,并且是 未定义的行为(不是程序)。仅以 thread-safe 方式处理共享数据。例如,如果您使用 mutex
来同步线程(如本例),则仅访问它 "under locked mutex" 以将同时使用共享数据的线程数限制为一个。如果您决定改用 atomic
- 请仅对 CAS (compare & swap) atomic operation 进行 检查 + increment/decrement。
run
也是如此(您也可以从不同的线程访问它,至少使其成为 volatile
)。注意:如果您使用我的代码,请不要忘记在将 run
设置为 false
时通知线程(我假设您在 exit()
中这样做)。
对于每个进入上半部分的球,每次迭代都会增加线上的球数。仅在越过线的那一刻执行此操作(我通过检查球在更新其位置之前和之后的位置来做到这一点,您也可以从之前的迭代中保存它以避免 double-check)。
我没有涉及可读性、风格等问题,只涉及主要的多线程和逻辑问题。
PS:
New balls are also appearing endlessly
因为你在这里不断地生成它们:
while(run) {
window->addBall();
threads.push_back(std::thread(ballFunction, i));
i++;
sleep(1);
}
我不知道你想让你的程序做什么。