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)。但我不想依赖那个。如果用户说关闭我想关闭它!

我发现的一种替代方法是使用 NetworkStream 的DataAvailable方法,这样我就可以在调用 StreamReader.Read() 之前对其进行检查。这不起作用,因为它不可靠,并且在从服务器读取数据包时丢失了数据。(因为我无法正确登录等等)

关于如何优雅地关闭这个线程的任何想法?我不想在监听线程上调用 Abort() ......

4

2 回答 2

2

真正唯一的答案是停止使用Read并切换到使用异步操作(即BeginRead)。这是一个更难使用的模型,但意味着没有线程被阻塞(即使客户端没有发送任何数据,您也不需要为每个客户端分配一个线程(一种非常昂贵的资源))。

顺便说一句,Thread.Sleep在并发代码中使用是一种难闻的气味(在重构意义上),它通常表明存在更深层次的问题(在这种情况下,应该进行异步、非阻塞操作)。

于 2009-12-31T08:53:00.883 回答
1

您实际上是在使用System.IO.StreamReaderSystem.IO.StreamWriter从套接字发送和接收数据吗?我不知道这是可能的。我只在'方法返回的对象上使用过Read()and方法。Write()NetworkStreamTcpClientGetStream()

假设这是可能的,StreamReader当到达流的末尾时返回 -1,而不是 0。所以在我看来,您的Read()方法处于无限循环中。

于 2009-12-31T17:18:12.957 回答