在我的应用程序中,我有几个用于处理TCP连接的线程(一个用于读取,一个用于发送,一个用于处理新的连接)。每个线程处理所有客户端的给定操作类型,因此假设它将数据发送到不同IP上的5个TcpClient
实例。我正在使用BlockingCollection
作为缓冲区,因为我从发送线程访问它,但也从另一个生成要发送的数据的线程中访问它。我在发送线程中运行的函数如下所示:
private void Sender()
{
while (run)
{
List<object[]> toRemove = new List<object[]>(); //bypass the fact that I cant remove a foreach iteration item from the BlockingCollection
foreach (object[] obj in sendBuffer.ToList())
{
string IP = obj[0].ToString();
string msg = obj[1].ToString();
byte[] data = Encoding.UTF8.GetBytes(msg);
foreach (TcpClient tcpc in TcpClients)
{
if ((tcpc.Client.RemoteEndPoint as IPEndPoint).Address.ToString() == IP)
{
NetworkStream stream = tcpc.GetStream();
stream.Write(data, 0, data.Length);
break;
}
}
toRemove.Add(obj);
}
for (int i = 0; i < toRemove.Count; i++) //the bypass mentioned above
{
object[] rmv = toRemove[i];
sendBuffer.TryTake(out rmv);
}
}
}
注意:使用的BlockingCollection
是<object[]>
类型。我的问题是,在某些流量点,缓冲区开始填满。我在缓冲区中设置了最多500条消息的限制,并且它很容易溢出。
现在,如果我理解正确(不确定),TryTake
会做什么,它会尝试删除该项,如果此刻正在使用该集合,它会等待并再次尝试。 (注意:我也尝试将超时设置为50毫秒)。如果这是真的(如果没有,有人请纠正我并提出不同的理由),问题可能是集合在大多数时间调用TryTake
时很忙。可能是那个吗?如果是的话,如何解决?
对于集合的使用,通过生成数据的线程,集合在foreach中每2秒访问一次,遍历1-80项的范围。缓冲区开始出现大约20多个问题,直到那时,它很好。 发送方线程现在只发送给一个客户端,之后它将最多发送到15个。因此在峰值中,这将是80个项目x 15个用户=每~2秒大约1200次访问。 非常感谢任何建议,谢谢。
答案 0 :(得分:1)
TryTake没有像您所描述的那样运行,默认的BlockingCollection使用ConcurrentQueue来存储项目,而TryTake会将队列中的下一个项目分配给提供的out参考。
例如
BlockingCollection<object[]> sendBuffer = new BlockingCollection<object[]>();
object[] message = new object[2];
object[] message2 = new object[2];
// Add messages to the queue
sendBuffer.Add(message);
sendBuffer.Add(message2);
object[] toSend;
// Take next message off the queue
sendBuffer.TryTake(out toSend);
// toSend === message
在您的情况下,您可以使用BlockingCollection.Take()等待发送消息:
BlockingCollection<object[]> sendBuffer = new BlockingCollection<object[]>();
// CancellationTokenSource is used in place of your run variable.
System.Threading.CancellationTokenSource cancellationSource
= new System.Threading.CancellationTokenSource();
private void Sender()
{
// Exit loop if cancellation requested
while (!cancellationSource.Token.IsCancellationRequested)
{
object[] obj;
try {
// Blocks until an item is available in sendBuffer or cancellationSource.Cancel() is called.
obj = sendBuffer.Take(cancellationSource.Token);
} catch (OperationCanceledException) {
// OperationCanceledException will be thrown if cancellationSource.Cancel()
// is called during call to sendBuffer.Take
break;
} catch (InvalidOperationException) {
// An InvalidOperationException means that Take() was called on a completed collection.
// See BlockingCollection<T>.CompleteAdding
break;
}
string IP = obj[0].ToString();
string msg = obj[1].ToString();
byte[] data = Encoding.UTF8.GetBytes(msg);
foreach (TcpClient tcpc in TcpClients) {
if ((tcpc.Client.RemoteEndPoint as IPEndPoint).Address.ToString() == IP) {
NetworkStream stream = tcpc.GetStream();
stream.Write(data, 0, data.Length);
break;
}
}
}
}