首页 > 解决方案 > 在 c++ ncurses 项目中使用互斥锁的正确方法?

问题描述

我找不到写一个好的条件来解决我的问题的方法。正如您在包含的图像中看到的那样,屏幕中间有一条线,球无休止地上下移动并在一段时间后消失。新球也层出不穷。

我的形象

我的任务是使用互斥锁,这样只有 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;
}

它根本不起作用。我是朝着正确的方向前进还是需要不同的方法?

标签: c++multithreadingmutex

解决方案


如果直接回答您的问题,我会使用condition_variable它。等到出现一个球的空闲位置(上半部分的球数小于球数限制)。当球离开限制球数区域时通知其他线程:

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;
}

但是代码有更多问题,我将解释最关键的问题。

首先,您counterballFunction(). 所有ballFunction调用者都有自己的版本counter,但您不想将它用作所有线程的单个共享变量。把它放在外面ballFunction()。你也用它来初始化它,2所以我假设你想把它用作线上的一些空闲位置。但是当球越过线并检查它是否与'2'和'3'相比时,你会增加它。做相反的事情 - 当球越过线时减少自由位置计数,并检查是否比0减少并让球进一步上升之前更多。

假设您修复了该问题,您仍然可以在counter没有任何关键部分处理和线程同步的情况下读取/写入该值。这可能导致数据竞争并且是未定义的行为(不是程序)。仅以线程安全的方式处理共享数据。例如,如果您用于mutex同步线程(如本例中),请仅“在锁定互斥锁下”访问它,以将同时处理共享数据的线程数限制为一个。如果您决定atomic改用 -仅使用CAS(比较和交换)原子操作进行检查 + 递增/递减

同样适用于run(您也可以从不同的线程访问它,至少 make it volatile)。注意:如果您使用我的代码,请不要忘记在设置为时通知线程(我假设您在 中这样做run) 。falseexit()

对于进入上部的每个球,每次迭代都会增加球的数量。仅在越线时执行此操作(我通过检查球在更新其位置之前的位置以及之后的位置来执行此操作,您还可以将其从先前的迭代中保存以避免重复检查)。

我没有触及可读性、风格等问题,只涉及主要的多线程和逻辑问题。

PS:

新球也层出不穷

因为你在这里无休止地产生它们:

while(run) {
    window->addBall();
    threads.push_back(std::thread(ballFunction, i));
    i++;
    sleep(1);
}

我不知道你想让你的程序做什么。


推荐阅读