我正在编写一个小型多线程网络服务器。所有经典的东西:它侦听传入的连接,接受它们,然后在不同的线程中提供它们。此外,此服务器有时必须重新启动,并且必须a)停止监听,b)踢出所有连接的客户端,c)调整一些设置/等待,d)恢复收听。
好吧,我几乎不知道开发多线程程序的事情,所以我正在寻求帮助。这就是我的目标(仅限核心内容):
class Server
{
class MyClient
{
Server server;
TcpClient client;
bool hasToFinish = false;
public MyClient(Server server, TcpClient client)
{
this.server = server;
this.client = client;
}
public void Go()
{
while (!hasToFinish)
{
// do all cool stuff
}
CleanUp();
}
private void CleanUp()
{
// finish all stuff
client.Close();
server.myClients.Remove(this);
}
public void Finish()
{
hasToFinish = true;
}
}
bool running = false;
TcpListener listener;
HashSet<MyClient> myClients = new HashSet<MyClient>();
public void Start()
{
if (running)
return;
myClients.Clear();
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
listener.Start();
listener.BeginAcceptTcpClient(AcceptClient, this);
running = true;
}
public void Stop()
{
if (!running)
return;
listener.Stop();
foreach (MyClient client in myClients)
{
client.Finish();
}
myClients.Clear();
running = false;
}
public void AcceptClient(IAsyncResult ar)
{
MyClient client = new MyClient(this, ((TcpListener)ar.AsyncState).EndAcceptTcpClient(ar));
myClients.Add(client);
client.Go();
}
}
绝对不能令人满意。没有同步(我只是不知道放在哪里!),并且调用Server.Stop()不会立即停止MyClient-s。我该如何解决这些问题?
答案 0 :(得分:1)
代码看起来非常干净,我们可以通过简单的修改使其成为线程安全的。
问题分为三部分,“客户端”,“服务器”和客户端 - 服务器交互。
客户端首先,Go()方法由一个线程调用(让我们称之为A),而Finish()方法由另一个线程调用(B)。当线程B修改hasToFinish字段时,线程A可能不会立即看到修改,因为该变量可能被缓存在CPU缓存中。我们可以通过使hasToFinish字段“volatile”来修复它,它强制线程B在更新时将变量更改发布到线程A.
现在是服务器类。我建议您在“服务器”实例上同步三个方法,如下例所示。它确保顺序调用Start和Stop,并且它们更改的变量将在线程之间发布。
还需要解决客户端 - 服务器交互问题。在您的代码中,客户端从服务器中删除其引用,但服务器在Finish()时以任何方式清除所有客户端引用。这看起来多余。如果我们可以删除客户端中的部分代码,我们无需担心。如果您选择将逻辑保留在客户端而不是服务器中,请在Server类中创建公共方法调用RemoveClient(客户端客户端)并将其与Server实例同步。然后让客户端调用此方法,而不是直接操作HashSet。
我希望这可以解决你的问题。
public void Start()
{
lock(this)
{
if (running)
return;
myClients.Clear();
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
listener.Start();
listener.BeginAcceptTcpClient(AcceptClient, this);
running = true;
}
}
public void Stop()
{
lock(this)
{
if (!running)
return;
listener.Stop();
foreach (MyClient client in myClients)
{
client.Finish();
}
myClients.Clear();
running = false;
}
}
public void AcceptClient(IAsyncResult ar)
{
lock(this)
{
MyClient client = new MyClient(this, ((TcpListener)ar.AsyncState).EndAcceptTcpClient(ar));
myClients.Add(client);
client.Go();
}
}