[英]Ensure queued messages are sent in correct order
我有一個客戶端-服務器體系結構,使用TCP進行通信。 客戶端以1msg / 70ms的速率向客戶端發送消息。 服務器必須模擬網絡延遲,因此在服務器接收方面,我執行以下操作:
//Event handler for new messages
private void DataReceived(object sender, DataReceivedEventArgs args)
{
//Add raw data to a queue for processing at a later time
TemporaryRawMessages.Enqueue(args.Bytes);
args.Recycle();
}
//Function running on thread; processes newly arrived raw messages
//new messages are removed from the queue, given their own thread and processed independently.
void ProcessMessagesIn()
{
while (true)
{
if (TemporaryRawMessages.Count > 0)
{
var raw = TemporaryRawMessages.Dequeue();
var t = new Thread(() => {
Thread.Sleep(LatencyUp);
var message = (ClientToServerMessage)Utils.Deserialize(raw_message);
Messages_In.Enqueue(message);
});
t.Start();
}
}
}
//Create & start a thread to processes incoming messages
var processMessagesIn = new Thread(ProcessMessagesIn);
processMessagesIn.Start();
注意LatencyUp
變量。 我希望來自客戶端的郵件延遲bx毫秒,但均勻間隔為1msg / 70ms。 換句話說,所有消息相隔LatencyUp
,但被LatencyUp
抵消。
我還向從服務器發送到客戶端的消息添加了延遲,以表示網絡停機延遲:
Task.Factory.StartNew(() => { SendLoop(); });
void SendToClient(ServerToClientMessage message)
{
Messages_Out.Enqueue(message);
}
void SendLoop()
{
while (true)
{
if(Messages_Out.Count > 0)
{
ServerToClientMessage message_out = null;
Messages_Out.TryDequeue(out message_out);
if(message_out != null)
{
Thread myNewThread = new Thread(() =>
{
Thread.Sleep(LatencyDown);
if (ClientConnection != null && ClientConnection.State == ConnectionState.Connected)
{
var serialized = Utils.Serialize(message);
ClientConnection.SendBytes(serialized, SendOption.Reliable);
}
});
myNewThread.Start();
}
}
}
}
我也嘗試過這樣發送,而沒有從服務器到客戶端的消息隊列:
void SendToClient(ServerToClientMessage message)
{
Thread myNewThread = new Thread(() =>
{
Thread.Sleep(LatencyDown);
Send(message);
});
myNewThread.Start();
}
問題
有時,從服務器到客戶端的消息會混雜在一起,例如,消息2可能在消息1之前發送。
我不確定為什么。 郵件以正確的順序放入隊列(我已經檢查過了)。 在添加到隊列與出隊+發送之間的某個地方出了點問題。
我唯一能想到的是一個線程在另一個線程之前完成,但是我不認為這將如何發生。
如何確保消息按發送順序(從客戶端到服務器)接收並以正確的順序從服務器發送到客戶端?
性能(就處理速度而言)很重要,因此,也應贊賞有關該主題的任何改進/建議。
您不能使用多個線程按順序分派事件。 如果您想為數據生產者提供一個異步使用者,那么您只需要一個線程即可。
創建生產者-消費者隊列的最簡單方法是使用BlockingCollection<T>
:
var blockingCollection = new BlockingCollection<int>();
// this is a single, one and only producer thread
Task.Run(() =>
{
var number = 0;
while (true)
{
number++;
Console.WriteLine("tx --> " + number);
blockingCollection.Add(number);
Task.Delay(1000).Wait();
}
});
// single, one and only consumer thread
foreach (var item in blockingCollection.GetConsumingEnumerable())
{
Task.Delay(200).Wait();
Console.WriteLine(" " + item + " --> rx");
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.