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 関数に問題があることがわかります。プログラムが 内のコード部分をロックしようとするとmove()、クラッシュします。たとえば、 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

コードが「爆発」する理由はわかりませんが、私にとっては問題なく動作するため、投稿したコードに問題はないと推測できます。

これは私のために出力されます:

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_NANOSLEEPgcc を使用していると仮定してコンパイルする必要があります。この質問を参照してください。)

上記のコード (おそらくあなたのコードも) はmove、ループが再びトリガーされる前に が 2 回以上呼び出されると、状態が無効になる可能性があるという問題に対して依然として脆弱であることに注意してください。
すでに述べたコメントの 1 つと同様に、lock_guards を使用することをお勧めします。

std::lock_guard<std::mutex> lock(mutal_state);
于 2012-03-09T21:44:42.533 に答える
2

私の提案:

1) デバッガーがない場合、最初の行でクラッシュしたことをどのように確認できますか? それを裏付ける確固たる証拠がない限り、コードについて行った仮定に常に疑問を投げかけます。

2) 状態 s3 にある興味深いコードを調べます。これは、move の最初の呼び出しが実行されるものです。その時点まで、s3 のコードは実行されていません。それを除外するか、投稿された例にあるすべてのコードバーを削除して、これを除外します。

3) コンパイラはレジスタ内の変数のコピーを作成する場合があります。この方法で最適化しないことを認識できるように、すべての状態を揮発性として宣言する必要があります。

于 2012-03-09T21:32:13.287 に答える
1

Linuxでg++を使用している場合、ミューテックスまたはスレッド化機能を正しく機能させるには、-lpthreadとリンクする必要があります。そうしないと、リンクに失敗することはありませんが、代わりに動作が悪くなるか、実行時にクラッシュします...

于 2012-03-09T22:32:44.930 に答える
1

私は自分の質問に答えています!私は問題を見つけたので、それはC++ 0xのロックやミューテックスの実装とは関係ありませんでした。の状態を制御するImageProcessクラスがありRobotます。タイプの親へのポインタがあり、それRobot*を使用してmove親になります。そのために、私は aworkhorseと a starter 関数を実装しました。startは a を生成し、その上でstd::tread実行workhorseします。

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

this->parent私は主力製品がダングリング ポインターであることに気付きました。明らかに、呼び出しparent->move()はクラッシュするはずです。しかし、すぐにはクラッシュしません。驚くべきことに、プログラム制御が機能に入り、存在しないものmove()を変更しようとします。(または、存在しないミューテックスをロックします)。previousStateRobotRobot

のようなスレッドを呼び出すstd::thread x(&ImageProcess::workhorse, *this); x.join() or x.detach()と、コードが呼び出し元オブジェクトで実行されなくなっていることがわかりました。thisテストするために、と&imageの両方にRobot::run()とのアドレスを出力しましたImageProcess::workhorse。違いました。また、 public ブール値を in に追加してfooそのImageProcess値をtrueinに変更しRobot、それをandworkhorserun出力しworkhorseまし0た。Robot1

これは非常に奇妙な動作だと思います。ImageProcessそれがメモリモデルに関連しているのか、それとも所有権が何らかの形で変更されたのかはわかりませんstd::thread x(&ImageProcess::workhorse, *this)...

ファクトリ パターン クラスを作成ImageProcessします (すべてが静的です!)。今は大丈夫です。

于 2012-03-10T13:10:29.600 に答える