这是我的情况:
我正在编写一个聊天客户端来连接到聊天服务器。我使用 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() ......