2

私はセマフォの実装スキームに興味があり、x86 では「ロック プレフィックス」を使用してアトミック操作を実装できることを知り、それを使用してミューテックスを実装したいと考えています。C++ 11 には標準のミューテックスがあることを知っています。 、しかし、私は自分自身を実装したいです。ここに私のコードがあります:

#include <iostream>
#include <thread>
#include <vector>

struct Semaphore
{
private:
    int s;
public:
    Semaphore( int s ) : s(s){}
    void wait()
    {
        int *p = &s;
        _asm
        {
            mov eax, p
            lock dec DWORD PTR [eax]
    begin : mov ebx, DWORD PTR [eax]
            cmp ebx, 0
            jl begin
        };
    }

    void signal()
    {
        int *p = &s;
        _asm
        {
            mov eax, p
            lock inc DWORD PTR [eax]
        };
    }
} s(1);

void print()
{
    s.wait();
    std::cout << "Print Thread " << std::this_thread::get_id() << std::endl;
    s.signal();
}

int main( int argc, char* argv )
{
    std::vector< std::thread > vec;
    int n = 3;
    for( int i = 0; i < n; ++i ) vec.push_back( std::thread( print ) );
    for( int i = 0; i < n; ++i ) vec[i].join();

    return 0;
}

問題は、2 つのスレッドがある場合、コードがうまくいくことですが、3 つのスレッドの場合、プログラムはデッドロック状態に陥っているように見えます。x86 マシンでの実装方法について誰かが理由を説明したり、提案をしたりできますか? ?

4

4 に答える 4

5

あなたwaitは本当にスピンロックです-ロックが競合している場合、他のスレッドがセマフォを解放するまで、CPUの100%を使用(しようとします)します。残念ながら、CPU の 100% を使用しているため、他のスレッドが CPU 時間を取得できず、デッドロックに近づいています。

推測では、おそらくデュアル コア CPU で実行しているでしょう。このような場合、スピンロックがタイトなループに収まり、CPU 時間を浪費していても、他のスレッドはフルスピードで実行できます。使用可能な CPU コアよりも多くのスレッドを取得すると、物事が停止します。

スピンロックは、セマフォがすぐにクリアされると確信できる十分な理由がある場合に役立ちます (この場合、タスク スイッチのオーバーヘッドを回避したい場合)。ただし、典型的なケースでは、「スピン」に費やされる時間を制限したいため、ループは次のようになります。

        mov ecx, 100
begin : mov ebx, DWORD PTR [eax]
        test ebx, ebx
        loopnz begin

次に、ループから抜け出した後、セマフォがクリアされたかどうか、または制限 (この場合は 100 回の反復) に達したかどうかを確認します。制限に達した場合は、スケジューラを呼び出して他のスレッドを実行させます (そして、このスレッドが再度実行されたときに待機を再試行します)。

于 2013-08-14T15:40:24.547 に答える
2

ここには複数の問題があります。ここに2つあります。

  1. あなたの wait() ルーチンは無条件にカウンターを減らします。ウェイターが 2 人いる場合、カウントは -2 になり、ウェイターが待機をやめる前に 2 つのシグナルが必要になります。

  2. 記述されているセマフォ コードは、スケジューラに完全に依存しています。したがって、スケジューラと、ウェイターとシグナルの優先順位によっては、待機中のタスク (ビジー ループ) が別の実行コンテキストに渡されない可能性が十分にあります。

お役に立てれば。

于 2013-08-14T15:50:31.217 に答える
2

作成したコードは、セマフォの適切な実装ではありません。セマフォは、待機中のタスクをセマフォの待機キューに入れることになっています。その後、セマフォが再度通知されるまでコードは実行されません。セマフォが通知されると、待機中のスレッドが起動されます。セマフォ コードの半分はカーネルにあり、そのアクセス方法の詳細はスレッド ライブラリの実装にあります。したがって、C++ でセマフォを適切に実装するには、もっと複雑なことを行う必要があります。または、独自のオペレーティング システムを作成することもできます。

また、使用しているコンパイラについては言及されていませんが、コンパイラが asm 句を極端に最適化している可能性があります。

于 2013-08-14T15:28:16.910 に答える
0

はい、OS やマシンの助けがなければ独自のバージョンを実装することはできなかったことを認めます。そのため、C++11 標準を使用して実装しようとしましたが、スタンフォード大学のコースで解決策が得られることがわかりました。私はそれを必要とする人と共有したいと思います。リンクは次のとおりです。

于 2013-08-15T08:05:38.917 に答える