这是我的情况:
我正在编写聊天客户端以连接聊天服务器。我使用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()方法是阻塞的。读()只是坐在那里,不会返回。因此,线程永远不会有机会睡眠1毫秒然后被打断。
我确定如果我让它足够长,我会得到另一个数据包并有机会让线程休眠(如果它是一个活跃的聊天室或从服务器获取ping)。但我不想依赖于此。如果用户说关闭我想关闭它!!
我发现的一个替代方法是使用NetworkStream的DataAvailable方法,以便在调用StreamReader.Read()之前检查它。这不起作用,因为它是不可靠的,我从服务器读取数据包时丢失了数据。 (因为我无法正确登录等,等等)
有关如何正常关闭此线程的任何想法?我不想在监听线程上调用Abort()...
答案 0 :(得分:2)
真正唯一的答案是停止使用Read
并切换到使用异步操作(即BeginRead
)。这是一个更难处理的模型,但意味着没有线程被阻止(即使客户端没有发送任何数据,也不需要为每个客户端专门设置一个线程 - 一个非常昂贵的资源)。
顺便说一句,在并发代码中使用Thread.Sleep
是一种难闻的气味(在重构意义上),它通常表示更深层次的问题(在这种情况下,应该进行异步,非阻塞,操作)。 / p>
答案 1 :(得分:1)
您实际上是使用System.IO.StreamReader
和System.IO.StreamWriter
从套接字发送和接收数据吗?我不知道这是可能的。我只使用了Read()
Write()
方法返回的NetworkStream
对象上的TcpClient
和GetStream()
方法。
假设这是可能的,StreamReader
在到达流的末尾时返回-1,而不是0.因此我认为你的Read()
方法处于无限循环中。