0

サーバーがすべてのクライアントのスレッドを持つクライアントサーバー構造を作成しています。その特定のスレッドは、データの送受信のみを行います。サーバーのメインスレッドで、クライアントスレッドが受け取った入力を読みたいと思います。しかし、メインスレッドが読み取りを行っているのと同時に、その入力がクライアントスレッドによって変更されている可能性があります。どうすればこれを防ぐことができますか? ロックについて読んだことがありますが、そのように実装する方法がわかりません。

私の質問の 2 番目の部分は次のとおりです。clientthread はネットワーク ストリームから常に読み取るループであるため、何かを読み取ることができるまでそのスレッドをブロックします。しかし、既存のクライアント スレッド (ループしている) が実行する必要がある関数をメイン スレッドから呼び出すことはできますか (その関数はそのネットワーク ストリームを介して何かを送信します)。

申し訳ありませんが、今はコードを提供できませんが、十分に明確だと思いますか?

4

1 に答える 1

1

生産者と消費者の設計が問題に適しているようです。一般的に、クライアント スレッドは受信したデータを (スレッド セーフな) キューに入れ、その後は変更しません。新しいデータが到着すると、キューの新しいスロットに移動します。その後、メイン スレッドは、いずれかのキューで新しいデータを待機し、到着したらそれを処理できます。メイン スレッドは、すべてのキューを定期的にチェックするか、(より良い) データがキューに配置されたときに何らかの通知を受信して​​、何も起こっていない間はスリープして CPU 時間を消費しないようにすることができます。

あなたはロックについて尋ねているので、これはキューの代替としての基本的なロックベースの実装です。おそらくそれは原理を理解するのに役立ちます

class IncomingClientData
{
    private List<byte> m_incomingData = new List<byte>();
    private readonly object m_lock = new object();

    public void Append(IEnumerable<byte> data)
    {
        lock(m_lock)
        {
            m_incomingData.AddRange(data);
        }
    }

    public List<byte> ReadAndClear()
    {
        lock(m_lock)
        {
            List<byte> result = m_incomingData;
            m_incomingData = new List<byte>();
            return result;
        }
    }
}

この例では、クライアント スレッドはAppend受信したデータを使用して を呼び出し、メイン スレッドは を呼び出して最後のチェック以降に受信したすべてのデータを収集できますReadAndClear

これは、両方の関数のすべてのコードを on にロックすることでスレッド セーフになりますm_lock。これは単なる通常のプレーン オブジェクトです。C# では任意のオブジェクトをロックできますが、これは混乱を招く可能性があり、不用意に使用すると微妙なバグにつながる可能性があると思います。そのため、ほとんどの場合、専用のオブジェクトを使用してロックします。一度に 1 つのスレッドのみがオブジェクトのロックを保持できるため、これらの関数のコードは一度に 1 つのスレッドでのみ実行されます。たとえばReadAndClear、クライアント スレッドがまだデータをリストに追加するためにビジー状態であるときにメイン スレッドが呼び出しを行う場合、メイン スレッドはクライアント スレッドがAppend関数を終了するまで待機します。

このために新しいクラスを作成する必要はありませんが、共有状態へのアクセス方法を注意深く制御できるため、事故を防ぐことができます。たとえば、ReadAndClear()その時点で同じリストへの参照が他にないため、内部リストを返すことが安全であることがわかっています。

2 番目の質問: メソッドを呼び出すだけでは、メソッドがどのクラスにあるかに関係なく、メソッドが別のスレッドで実行されることはありません。これInvokeは WinForms UI スレッドの特別な機能であり、実装する必要があります。Invokeワーカースレッドで何かしたい場合は、その機能を自分で。初めの、Invoke実行したいコードを、UI イベントなど、UI スレッドで実行されるはずのすべてのもののキューに配置することで機能します。UI スレッド自体は、基本的に、常にそのキューから次の作業を取得し、その作業を実行してから、キューから次の項目を取得するというループです。これが、イベント ハンドラーで長時間の作業を行うべきではない理由でもあります。UI スレッドがコードの実行でビジーである限り、キュー内の次の項目を処理できないため、すべての処理が滞ります。発生するその他の作業項目/イベント。

クライアント スレッドに特定の機能を実行させたい場合は、そのためのコードを実際に提供する必要があります。たとえば、クライアント スレッドがメイン スレッドからのコマンドのキューをチェックするようにします。

于 2013-12-15T13:21:07.470 に答える