2

WinSock2とWinAPI関数を使用してチャットを書いています。そして、私は少し問題があります。
クライアント接続のstd::vectorをサーバーに保存します。新しいクライアントが接続すると、新しいスレッドが開始され、クライアントとのすべての作業がこの新しいスレッドで行われます。私はクラスを使用しないので(あまり良くないことはわかっています)、この接続のリストはグローバル変数として定義されています。
複数のスレッドがこのリストに同時にアクセスしようとする状況になる可能性があるように思われます。問題があることに気づいていませんが、次のようなことをする必要がありますか?


template 
class SharedVector {
    std::vector vect;
    CRITICAL_SECTION cs;
    SharedVector(const SharedVector& rhs) {}
public:
    SharedVector();
    explicit SharedVector(const CRITICAL_SECTION& CS);
    void PushBack(const T& value);
    void PopBack();
    unsigned int size();
    T& operator[](int index);
    virtual ~SharedVector();
};

template
SharedVector::SharedVector() {
    InitializeCriticalSection(&cs);
}

template
SharedVector::SharedVector(const CRITICAL_SECTION& r): cs(r) {
    InitializeCriticalSection(&cs);
}

template
void SharedVector::PushBack(const T& value) {
    EnterCriticalSection(&cs);
    vect.push_back(value);
    LeaveCriticalSection(&cs);
}

template
void SharedVector::PopBack() {
    EnterCriticalSection(&cs);
    vect.pop_back();
    LeaveCriticalSection(&cs);
}

それで、私の状況ではCRITICAL_SECTIONを使用する必要がありますか?私は間違いを見つけなかった幸運な人ですか?

4

5 に答える 5

7

はい、問題が発生しないのは幸運です。これは、同期の問題と競合状態の問題であり、すべてのケースの 99.9% でコードが機能し、災害が発生したときにその理由がわからないという問題です。

コンストラクターがそれを初期化することを理解するために (おそらく存在しない) ドキュメントを見ないと明確ではないため、CRITICAL_SECTION をパラメーターとして取るコンストラクターを取り除きます。

于 2008-12-26T18:10:34.687 に答える
3

私があなたに持っている最大の質問は建築です。各接続のスレッドは、この他の接続の配列に直接アクセスする必要がありますか? 彼らは本当にある種の抽象的なメッセージをキューに入れるべきではないでしょうか? 各接続スレッドが、それが存在するユニバースの実装の詳細を意識している場合、どこかで問題が発生することが予想されます。

しかし、接続スレッドが実際に相互に直接アクセスする必要があると仮定しましょう...

グローバル変数は本質的に悪ではありません。学校は、微妙な理解を提供するよりも簡単であるため、それを教えているだけです。グローバル変数の正確な意味を知る必要があります。代替手段がないことがわかるまで、グローバル変数は間違った選択であると想定するのは適切な経験則です。多くの問題を解決する 1 つの代替手段は、次のような関数を作成することです。

SharedVector & GetSharedVector (void)
{
    static SharedVector sharedVector;
    return sharedVector;
}

これにより、グローバル変数を使用する場合よりも、クラスがインスタンス化されるタイミングをより詳細に制御できます。これは、コンストラクターを持つグローバル変数間に依存関係がある場合、特にそれらのグローバル変数がスレッドを生成する場合に重要になる可能性があります。また、誰かがこの「グローバル」変数にアクセスしたいときに介入する機会を与えてくれます。直接突っついている間、無力に苦しむのではなく。

他にもいろいろな考えが頭に浮かびます。順不同で・・・

  • ここで使用するテンプレート構文が奇妙であることはすでにご存じでしょう。このコードをコンパイルせずに入力しただけだと仮定します。
  • 事故を防ぐために、プライベート セクションに何もしない代入演算子が必要です (何もしないコピー コンストラクターがあるのと同じ理由で)。
  • 明示的なコピー コンストラクターは、さまざまな点で壊れています。ベクターを明示的にコピーする必要があります。そうしないと、SharedVector の新しいインスタンスに空のベクターが作成されます。また、クリティカル セクションをコピーする (そして初期化する) のではなく、単に自身のクリティカル セクションを初期化する必要があります。本当にあなたの場合、ネットワーク接続には合理的なコピーセマンティクスがないため、この機能を使用することはおそらくまったく意味がありません。
  • コンストラクタが EnterCriticalSection を呼び出し、デストラクタが LeaveCriticalSection を呼び出すクラス EnteredCriticalSection を作成することで、例外の安全性をより簡単にすることができます。(もちろん、クリティカル セクションへの参照を保持する必要があります。) これにより、例外に直面した場合に、他のメンバー関数を安全にシリアル化することが非常に簡単になります。
于 2009-01-08T03:02:46.310 に答える
3

このコードは例外セーフではありません。ベクトルの push_back および pop_back メソッドは例外をスローする可能性があり、ここでデッドロックが発生する可能性があります。このメソッド:

unsigned int size();
T& operator[](int index);

別のスレッドで変更できるデータにアクセスするため、これらも同期する必要があります。たとえば、次のようにデータにアクセスできます。

value = shared_vector[shared_vector.size() - 1];

同時に、別のスレッドがこれを実行できます。

shared_vector.PopBack();
于 2008-12-26T19:23:10.810 に答える
1

はい、このようなグローバル ベクトルを公開する場合は、読み取り/書き込み時に必ずロックする必要があります。そして、はい、それはあなたのパフォーマンスをひどく損なう可能性があります.

また、リクエストごとに 1 つの新しいスレッドを作成することも、通常はあまり良い考えではありません。代わりに IO Completion ポートを使用するようにアプリケーションを再設計してみませんか?

于 2008-12-26T18:09:22.333 に答える
0

ところで、このベクトルをグローバル変数として宣言するのは良い方法ではありません。ローカルにしたほうがいいのでしょうか?

于 2008-12-26T18:15:49.403 に答える