0

これが私の状況です:

チャットサーバーに接続するためのチャットクライアントを作成しています。TcpClientを使用して接続を作成し、そこからNetworkStreamオブジェクトを取得します。StreamReaderとStreamWriterを使用して、データの読み取りと書き込みを行っています。

これが私の読み取りがどのように見えるかです:

public string Read()
{
 StringBuilder sb = new StringBuilder();
 try
 {
  int tmp;
  while (true)
  {                
   tmp = StreamReader.Read();
   if (tmp == 0)
    break;
   else
    sb.Append((char)tmp);
   Thread.Sleep(1);
  }
 }
 catch (Exception ex)
 {
  // log exception
 }
 return sb.ToString();
}

それはうまく機能し、ダンディです。私のメインプログラムでは、このReadメソッドを継続的に呼び出してデータがあるかどうかを確認するスレッドを作成します。例を以下に示します。

private void Listen()
{
 try
 {
  while (IsShuttingDown == false)
  {
   string data = Read();
   if (!string.IsNullOrEmpty(data))
   {
    // do stuff
   }
  }
 }
 catch (ThreadInterruptedException ex)
 {
  // log it
 }
}

...

Thread listenThread = new Thread(new ThreadStart(Listen));
listenThread.Start();

これは問題なく機能します。アプリケーションをシャットダウンしたいときに問題が発生します。UIからシャットダウンコマンドを受け取り、リスニングスレッドにリスニングを停止するように指示します(つまり、この読み取り関数の呼び出しを停止します)。Joinを呼び出し、この子スレッドの実行が停止するのを待ちます。そのようです:

// tell the thread to stop listening and wait for a sec
IsShuttingDown = true;            
Thread.Sleep(TimeSpan.FromSeconds(1.00));

// if we've reach here and the thread is still alive
// interrupt it and tell it to quit
if (listenThread.IsAlive)
    listenThread.Interrupt();

// wait until thread is done
listenThread.Join();

問題は、実行が停止しないことです。コードにステップインしましたが、Read()メソッドがブロックしているため、リスニングスレッドがブロックしています。Read()はそこに座っているだけで、戻りません。したがって、スレッドがその1ミリ秒スリープしてから中断されることはありません。

十分な時間放置すると、別のパケットを取得してスレッドをスリープ状態にする機会が得られると確信しています(アクティブなチャットルームの場合、またはサーバーからpingを取得する場合)。しかし、私はそれに依存したくありません。ユーザーがシャットダウンと言ったら、シャットダウンしたいです!!

私が見つけた代替案の1つは、NetworkStreamのDataAvailableメソッドを使用して、StreamReader.Read()を呼び出す前にチェックできるようにすることです。信頼性が低く、サーバーからパケットを読み取るときにデータが失われたため、これは機能しませんでした。(そのため、正しくログインできなかったなど)

このスレッドを正常にシャットダウンする方法についてのアイデアはありますか?リスニングスレッドでAbort()を呼び出すのは嫌です...

4

2 に答える 2

2

本当に唯一の答えは、使用をやめてRead非同期操作 (つまり ) の使用に切り替えることBeginReadです。これは扱いにくいモデルですが、スレッドがブロックされないことを意味します (また、クライアントがデータを送信していない場合でも、スレッド (非常に高価なリソース) を各クライアント専用にする必要はありません)。

ところで、Thread.Sleep並行コードでの使用は (リファクタリングの意味で) 悪臭を放ちますが、通常はより深い問題を示しています (この場合は、非同期でノンブロッキングの操作を行う必要があります)。

于 2009-12-31T08:53:00.883 に答える
1

実際にSystem.IO.StreamReaderandを使用System.IO.StreamWriterして、ソケットからデータを送受信していますか? これが可能だとは知りませんでした。のメソッドによって返されるオブジェクトに対してRead()およびWrite()メソッドのみを使用したことがあります。NetworkStreamTcpClientGetStream()

これが可能であると仮定すると、ストリームの終わりに到達すると0ではなく-1が返されます。したがって、メソッドが無限ループにあるStreamReaderように見えます。Read()

于 2009-12-31T17:18:12.957 に答える