C# 数据库阻塞线程对大量客户端不利
C# Database Blocking Threads bad for large amounts of clients
信息:
我已经为公司创建了一个 C# 控制台服务器和 WinForms 客户端。
(如果我错了请纠正我,我做了很多研究。所以我可能混淆了某些主题。)
在服务器上,我读到使用 Socket.BegingReceive
和 AsyncCallback
方法是最好的。由于它使用 ThreadPool
class。对客户端进行多线程处理,同时也重用线程等。(而不是在单线程设置或每个客户端线程上执行 while 循环。(我读到这些有我不感兴趣的特定缺点。))
基本上我一直在努力的是确保如果我正在制作的应用程序突然爆炸并且有 100,000(甚至可能是 1,000,000?!)人们同时使用它,它会保持良好和稳定的连接每个客户。
情况:
服务器: Intel i5 4 Core: 服务器在另一台服务器上处理 MYSQL 请求并将其返回给客户按照他们期望的方式格式化。
客户端: 无限硬件资源:浏览表单填写表单时向服务器请求数据
所以我担心: 假设有 4 个客户端连接并请求服务器处理数据,这大约需要 5 秒来处理 MYSQL 数据库(其中,我知道,就像处理速度的一生。(最坏情况等))。如果第 5 个客户端连接怎么办。这意味着 ThreadPool 必须创建另一个线程来保持这个进程。现在让这些数字变大,我有 100 个并发客户端请求 exact long 运行 方法。在 4 核 CPU 上。所以现在 ThreadPool 花费了十秒以上的时间来创建所有这些新线程。
问题:
编辑:缩小问题范围:
我如何为 FindPrimeNumbers 调用实现异步编程。 (该方法是在数据库(MySQL 在不同的服务器上,如果有帮助的话)搜索数字。)
我该怎么做才能防止这种情况发生?我如何使它更直观。如何防止大负载潜在地造成糟糕的用户体验等等
Sudo 代码
FindPrimerNumber 方法类似于数据库调用。
class Program
{
private static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static List<Socket> clientSockets = new List<Socket>();
private static CancellationTokenSource CancelSource = new CancellationTokenSource();
private static CancellationToken CancelToken = CancelSource.Token;
private const int BUFFER_SIZE = 2048;
private const int PORT = 100;
private static readonly byte[] buffer = new byte[BUFFER_SIZE];
static void Main()
{
SetupServer();
Console.ReadLine();
CloseAllSockets();
}
private static void SetupServer()
{
serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT));
serverSocket.Listen(0);
serverSocket.BeginAccept(BeginAcceptCallback, null);
}
private static void CloseAllSockets()
{
foreach (Socket socket in clientSockets)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
serverSocket.Close();
}
private static void BeginAcceptCallback(IAsyncResult AR)
{
Socket socket = serverSocket.EndAccept(AR);
clientSockets.Add(socket);
socket.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, BeginReceiveCallback, socket);
Console.WriteLine("Client connected!");
serverSocket.BeginAccept(BeginAcceptCallback, null);
}
private static void BeginReceiveCallback(IAsyncResult AR)
{
Socket current = (Socket)AR.AsyncState;
int receivedInt = current.EndReceive(AR);
byte[] received = new byte[receivedInt];
Array.Copy(buffer, received, receivedInt);
current.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, BeginReceiveCallback, current);
string text = Encoding.ASCII.GetString(received);
int number = 0;
if (text.ToLower() == "exit")
{
current.Shutdown(SocketShutdown.Both);
current.Close();
clientSockets.Remove(current);
Console.WriteLine("Client Disconnected");
return;
}
else if (int.TryParse(text, out number))
{
int num = FindPrimeNumber(number);
byte[] data = Encoding.ASCII.GetBytes(num.ToString());
current.Send(data);
Console.WriteLine("Prime Number: "+num+" :Sent");
}
else
{
current.Send(Encoding.ASCII.GetBytes("Not a Command"));
}
}
//VERY LONG CPU FUNCTION
public static int FindPrimeNumber(int n)
{
//Connect to database (MySQL Database on a different server, if that makes a difference)
//Send Query
//Read Return
return queryReturn()
}
}
您有一个线程 运行 的事实并不意味着该线程必须在核心上消耗 100% 的时钟周期。启动诊断工具(进程查看器或其他工具)并查看机器上的线程数:将有数百个。再看看总的CPU消耗:接近于零。绝大多数线程将大部分时间用于等待其他事情发生。
另一方面,如果你有一个查询在你的数据库服务器上执行需要长达 5 秒,那你就有麻烦了,因为这意味着你对它施加了太多的压力,所以性能无论并行化如何,整个系统都会受到影响。
这些都是很基础的东西,所以如果你还没有掌握它,那么担心它是不合时宜和不切实际的。只做你必须做的,以后再担心性能。
信息:
我已经为公司创建了一个 C# 控制台服务器和 WinForms 客户端。
(如果我错了请纠正我,我做了很多研究。所以我可能混淆了某些主题。)
在服务器上,我读到使用 Socket.BegingReceive
和 AsyncCallback
方法是最好的。由于它使用 ThreadPool
class。对客户端进行多线程处理,同时也重用线程等。(而不是在单线程设置或每个客户端线程上执行 while 循环。(我读到这些有我不感兴趣的特定缺点。))
基本上我一直在努力的是确保如果我正在制作的应用程序突然爆炸并且有 100,000(甚至可能是 1,000,000?!)人们同时使用它,它会保持良好和稳定的连接每个客户。
情况:
服务器: Intel i5 4 Core: 服务器在另一台服务器上处理 MYSQL 请求并将其返回给客户按照他们期望的方式格式化。
客户端: 无限硬件资源:浏览表单填写表单时向服务器请求数据
所以我担心: 假设有 4 个客户端连接并请求服务器处理数据,这大约需要 5 秒来处理 MYSQL 数据库(其中,我知道,就像处理速度的一生。(最坏情况等))。如果第 5 个客户端连接怎么办。这意味着 ThreadPool 必须创建另一个线程来保持这个进程。现在让这些数字变大,我有 100 个并发客户端请求 exact long 运行 方法。在 4 核 CPU 上。所以现在 ThreadPool 花费了十秒以上的时间来创建所有这些新线程。
问题:
编辑:缩小问题范围:
我如何为 FindPrimeNumbers 调用实现异步编程。 (该方法是在数据库(MySQL 在不同的服务器上,如果有帮助的话)搜索数字。)
我该怎么做才能防止这种情况发生?我如何使它更直观。如何防止大负载潜在地造成糟糕的用户体验等等
Sudo 代码
FindPrimerNumber 方法类似于数据库调用。
class Program
{
private static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static List<Socket> clientSockets = new List<Socket>();
private static CancellationTokenSource CancelSource = new CancellationTokenSource();
private static CancellationToken CancelToken = CancelSource.Token;
private const int BUFFER_SIZE = 2048;
private const int PORT = 100;
private static readonly byte[] buffer = new byte[BUFFER_SIZE];
static void Main()
{
SetupServer();
Console.ReadLine();
CloseAllSockets();
}
private static void SetupServer()
{
serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT));
serverSocket.Listen(0);
serverSocket.BeginAccept(BeginAcceptCallback, null);
}
private static void CloseAllSockets()
{
foreach (Socket socket in clientSockets)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
serverSocket.Close();
}
private static void BeginAcceptCallback(IAsyncResult AR)
{
Socket socket = serverSocket.EndAccept(AR);
clientSockets.Add(socket);
socket.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, BeginReceiveCallback, socket);
Console.WriteLine("Client connected!");
serverSocket.BeginAccept(BeginAcceptCallback, null);
}
private static void BeginReceiveCallback(IAsyncResult AR)
{
Socket current = (Socket)AR.AsyncState;
int receivedInt = current.EndReceive(AR);
byte[] received = new byte[receivedInt];
Array.Copy(buffer, received, receivedInt);
current.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, BeginReceiveCallback, current);
string text = Encoding.ASCII.GetString(received);
int number = 0;
if (text.ToLower() == "exit")
{
current.Shutdown(SocketShutdown.Both);
current.Close();
clientSockets.Remove(current);
Console.WriteLine("Client Disconnected");
return;
}
else if (int.TryParse(text, out number))
{
int num = FindPrimeNumber(number);
byte[] data = Encoding.ASCII.GetBytes(num.ToString());
current.Send(data);
Console.WriteLine("Prime Number: "+num+" :Sent");
}
else
{
current.Send(Encoding.ASCII.GetBytes("Not a Command"));
}
}
//VERY LONG CPU FUNCTION
public static int FindPrimeNumber(int n)
{
//Connect to database (MySQL Database on a different server, if that makes a difference)
//Send Query
//Read Return
return queryReturn()
}
}
您有一个线程 运行 的事实并不意味着该线程必须在核心上消耗 100% 的时钟周期。启动诊断工具(进程查看器或其他工具)并查看机器上的线程数:将有数百个。再看看总的CPU消耗:接近于零。绝大多数线程将大部分时间用于等待其他事情发生。
另一方面,如果你有一个查询在你的数据库服务器上执行需要长达 5 秒,那你就有麻烦了,因为这意味着你对它施加了太多的压力,所以性能无论并行化如何,整个系统都会受到影响。
这些都是很基础的东西,所以如果你还没有掌握它,那么担心它是不合时宜和不切实际的。只做你必须做的,以后再担心性能。