【发布时间】:2014-01-11 00:36:36
【问题描述】:
您好,我在处理我的项目时有些头疼。
简短总结:
- 客户端/服务器应用程序(我在服务器端)
- 多线程
- 使用 System.Timers.Timer 每秒保持活动状态
- 单独线程上的主网络循环(从客户端读取/写入数据包)
- 单独线程上的服务器(此时无关紧要)
我有一个处理所有客户端的 ClientHandler 类。 (Networker 是主循环) ClientList 是这样实现的:
public List<Client> ClientList { get; private set; }
每次我尝试访问 ClientList(读/写)时,我都会使用...
lock(ClientList){}
lock(ClientHandler.ClientList){}
... 取决于我在 ClientHandler 内部还是外部。
到目前为止,我没有使用任何锁,因此存在一些并发问题。
但是现在当我使用/误用锁时,我遇到了一些保活问题。
如果客户端连接:
public bool AddClient(Client client)
{
lock (ClientList)
{
if (client == null || ClientList.Contains(client))
return false;
ClientList.Add(client);
return true;
}
}
而且我的计时器每一秒都会加入一个keepalive:
private void KeepAliveTimer_Elapsed(object sender, ElapsedEventArgs e)
{
KeepAliveTimer.Stop();
lock (ClientList)
{
if (ClientList.Count > 0)
{
foreach (Client client in ClientList)
{
lock (client)
{
client.PacketQueue.Enqueue(new KeepAlivePacket());
}
}
}
}
KeepAliveTimer.Start();
}
还有我当前的主循环:
private void Networker()
{
while (IsRunning)
{
lock (ClientHandler.ClientList)
{
if (ClientHandler.ClientList.Count > 0)
{
foreach (Client client in ClientHandler.ClientList)
{
// Check if client has data to read.
// Read for up to 10 msecs.
if (client.DataAvailable)
{
DateTime expiry = DateTime.Now.AddMilliseconds(10);
while (DateTime.Now <= expiry)
{
int id = client.ReadByte();
if (id == -1 || !PacketHandler.HandlePacket((byte)id, client, this))
{
ClientHandler.DisconnectClient(client);
continue;
}
}
}
// Check if client has data to write.
// Write for up to 10 msecs.
if (client.PacketQueue.Count > 0)
{
DateTime expiry = DateTime.Now.AddMilliseconds(10);
while (DateTime.Now <= expiry && client.PacketQueue.Count > 0)
{
IPacket packet = client.PacketQueue.Dequeue();
if (!packet.Write(client))
{
ClientHandler.DisconnectClient(client);
continue;
}
}
}
}
}
}
Thread.Sleep(1);
}
}
在所有这些锁定之前,我的测试客户端每秒都会收到一个 KeepAlivePacket。
现在我只得到它一次,因为在第一个 KeepAlivePacket KeepAliveTimer_Elapsed 之后无法再访问锁,因为它被其他线程永久锁定(用一些调试输出对其进行了测试)。
提供的代码中是否有某些内容可能是疯子,或者我做错了什么?
如果有人能让我摆脱这种痛苦,那就太好了。
编辑(感谢 Joachim Isaksson):
我不知道这是否是唯一的错误,但我忘记的一件事是在我读取第一个数据包后检查主循环是否有可用数据。
这是第一个问题,因为我只用我的 TestClient 发送了一个数据包,而服务器卡在了 client.ReadByte,因为事先没有检查。
if (client.DataAvailable)
{
DateTime expiry = DateTime.Now.AddMilliseconds(10);
while (DateTime.Now <= expiry && client.DataAvailable)
{
try
{
int id = client.ReadByte();
// do stuff...
}...
}
}
【问题讨论】:
-
与论坛网站不同,我们不使用“谢谢”、“感谢任何帮助”或Stack Overflow 上的签名。请参阅“Should 'Hi', 'thanks,' taglines, and salutations be removed from posts?.
-
好的,谢谢,记住这一点:)
-
不确定您呼叫的是哪个
ReadByte,但我怀疑它会等待数据可用。如果没有,它将锁定直到有,并且永远不会释放锁定。 -
我会检查一下。它是 NetworkStream.ReadByte 的包装器。我这样做是为了在后台进行异常处理。我只在主循环中检查返回值。
标签: c# multithreading locking