9

我有一个代表有限状态机的类,它应该在一个永远循环中运行并检查它的当前状态。在每个状态机将设置它的下一个状态,要么进入idle状态,要么做一些工作。我想允许另一个线程在它工作时改变机器的状态。正如预期的那样,这将导致竞争条件。所以我添加了一个互斥锁/解锁机器的包装循环和允许其他线程改变机器当前状态的公共方法。

class Robot
{
public:
    enum StateType {s1,s2,s3,idle,finish};
    void run();
    void move();
private:
    StateType currentState;
    StateType nextState;
    StateType previousState;
    std::mutex mutal_state;
};

执行:

void Robot::run()
{
    this->currentState = s1;
    while(true)
    {
        mutal_state.lock();
        switch(currentState)
        {
        case s1:
            // do some useful stuff here...
            currentState = idle;
            nextState = s3;
            break;
        case s2:
            // do some other useful stuff here...
            currentState = idle;
            nextState = finish;
            break;
        case s3:
            // again, do some useful things...
            currentState = idle;
            nextState = s2;
            break;
        case idle:
            // busy waiting...
            std::cout << "I'm waiting" << std::endl;
            break;
        case finish:
            std::cout << "Bye" << std::endl;
            mutal_state.unlock();
            return;
        }
        mutal_state.unlock();
    }
}

以及允许其他线程改变当前状态的 move 方法:

void Robot::move()
{
    mutal_state.lock();
    previousState = currentState; // Booommm
    currentState = nextState;
    mutal_state.unlock();
}

我无法找到我做错了什么!move()程序在函数的第一行崩溃。另一方面,GDB 不适用于 C++11,并且无法跟踪代码......

更新:

玩弄代码,我可以看到问题出在移动功能上。当程序尝试将代码块锁定在内部move()时,会崩溃。例如,如果移动是这样的:

void Robot::move()
{
    std::cout << "MOVE IS CALLED" << std::endl;
    mutal_state.lock();
    //previousState = currentState;
    //std::cout << "MOVING" << std::endl;
    //currentState = nextState;
    mutal_state.unlock();
}

输出是:

s1
I'm waiting
I'm waiting
MOVE IS CALLED1
The program has unexpectedly finished.

但是 whenmove是一个简单的函数,什么都不做:

void Robot::move()
{
    std::cout << "MOVE IS CALLED" << std::endl;
    //mutal_state.lock();
    //previousState = currentState;
    //std::cout << "MOVING" << std::endl;
    //currentState = nextState;
    //mutal_state.unlock();
}

程序同时运行。

4

4 回答 4

2

我的建议:

1)如果你没有调试器,你怎么能确定这是第一行崩溃?除非你有确凿的证据支持它,否则总是质疑你对代码所做的任何假设。

2) 我会查看状态 s3 中的任何有趣代码,因为这是第一次调用 move 将执行的操作。到目前为止,s3 中的代码尚未运行。要么删除所有代码栏,要么删除发布的示例中的所有代码栏,以排除这种情况。

3) 编译器可能会复制寄存器中的变量,您应该将所有状态声明为 volatile,这样它就知道不要以这种方式进行优化。

于 2012-03-09T21:32:13.287 回答
2

我无法帮助您为什么您的代码“爆炸”,但是我可以假设问题不在您发布的代码中,因为它对我来说运行良好。

这将为我输出:

I'm working
...
Bye

代码:

int main() {

    Robot r;

    auto async_moves = [&] () {  // simulate some delayed interaction
        std::this_thread::sleep_for(std::chrono::seconds(2)); //See note
        for(auto i = 0; i != 3; ++i)
            r.move();

    };

    auto handle = std::async(std::launch::async, async_moves);

    r.run();

} 

(注意:您必须-D_GLIBCXX_USE_NANOSLEEP使用 gcc 进行编译,请参阅问题。)

请注意,上面的代码 - 可能还有你的代码 - 仍然容易受到问题的影响,如果move在循环再次触发之前被调用两次或更多次,状态可能会失效。
就像已经提到的评论之一,更喜欢使用 lock_guards:

std::lock_guard<std::mutex> lock(mutal_state);
于 2012-03-09T21:44:42.533 回答
1

如果您在 linux 上使用 g++,则需要链接 -lpthread 以使互斥锁或线程的东西正常工作。如果你不这样做,它不会链接失败,而是会在运行时表现不佳或崩溃......

于 2012-03-09T22:32:44.930 回答
1

我在回答我自己的问题!因为我发现了问题,而且它与 C++0x 的锁定和互斥实现无关。有一个ImageProcess类应该控制Robot. 它有一个指向它的父类型的指针,Robot*并且使用它,move它的父。为此,我实现了一个workhorse和一个starter 函数。start生成 astd::tread并在其workhorse上运行:

void ImageProcess::start()
{
    std::thread x(&ImageProcess::workhorse, *this);
    x.detach();
}

我意识到this->parent在主力中是一个悬空指针。显然调用parent->move()应该崩溃。但它不会立即崩溃!令人惊讶的是,程序控制进入move()功能,然后试图改变previousState一个不存在Robot的东西。(或锁定不存在的互斥锁Robot)。

我发现在调用类似的线程时std::thread x(&ImageProcess::workhorse, *this); x.join() or x.detach(),代码不再在调用者对象中运行。为了测试我在两者中都打印了thisand的地址and 。有不同的。我还添加了一个公共布尔值并将其值更改为in ,然后将其打印在and中,in value always is but in is 。&imageRobot::run()ImageProcess::workhorsefooImageProcesstrueRobotworkhorserunworkhorse0Robot1

我相信这是非常奇怪的行为。我不知道它是否与内存模型或所有权以ImageProcess某种方式更改后std::thread x(&ImageProcess::workhorse, *this)...

我制作ImageProcess了一个工厂模式类(一切都是静态的!)。现在好啦。

于 2012-03-10T13:10:29.600 回答