在C ++ ncurses项目中使用互斥的正确方法?

时间:2019-05-08 14:07:45

标签: c++ multithreading mutex

我找不到写出好的条件来解决我的问题的方法。如您所见,在屏幕的中间有一条线,球不断地向上和向下移动,并在一段时间后消失。新球也不断出现。

my image

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

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

1 个答案:

答案 0 :(得分:2)

如果直接回答您的问题,我会使用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 (compare & swap) atomic operation进行 check +增量/减量

run也是如此(您也可以从不同的线程访问它,至少使其成为volatile)。注意:如果使用我的代码,将run设置为false时,不要忘记通知线程(我假设您在exit()中这样做)。

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

我没有涉及可读性,样式等问题,只有主要的多线程和逻辑问题。

PS:

  

新球也不断出现

因为您在这里不断产生它们:

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

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