消息服务器,生产者和消费者,消息队列
ChatServer\ChatConnection.cs
using System.Runtime.CompilerServices;
using System.Threading.Channels;
public class ChatConnection : IObserver<ChatMessage>
{
private const int MaxStuckMessages = 10;
private readonly Channel<ChatMessage> _channel;
public ChatConnection()
{
// - 构造函数,初始化 `_channel` 为有界队列,最多缓存 10 条消息。
// - `AllowSynchronousContinuations = false`:不允许同步执行回调,避免死锁。
// - `FullMode = BoundedChannelFullMode.DropOldest`:队列满时丢弃最早的消息。
// - `SingleReader = true`:只有一个消费者读取消息。
// - `SingleWriter = true`:只有一个生产者写入消息。
_channel = Channel.CreateBounded<ChatMessage>(
new BoundedChannelOptions(MaxStuckMessages)
{
AllowSynchronousContinuations = false,
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = true
}
);
}
public void OnCompleted() => _channel.Writer.TryComplete();
public void OnError(Exception error) => _channel.Writer.TryComplete(error);
public void OnNext(ChatMessage value) => _channel.Writer.TryWrite(value);
public async IAsyncEnumerable<ChatMessage> AsEnumerable([EnumeratorCancellation] CancellationToken cancellationToken)
{
// - `AsEnumerable` 方法返回一个异步可枚举的消息流,支持取消。
// - 外层 `while` 循环:只要没有取消,就持续等待新消息。
// - `WaitToReadAsync`:异步等待队列中有消息可读。
// - 捕获 `OperationCanceledException`,如果被取消则退出循环。
// - 如果队列已关闭且无消息可读,则退出循环。
// - 内层 `while` 循环:只要有消息可读且未取消,就不断读取并 `yield return` 返回消息。
while (!cancellationToken.IsCancellationRequested)
{
bool flag;
try
{
flag = await _channel.Reader.WaitToReadAsync(cancellationToken);
}
catch (OperationCanceledException)
{
flag = false;
}
if (!flag)
break;
while (!cancellationToken.IsCancellationRequested && _channel.Reader.TryRead(out var message))
{
yield return message;
}
}
}
}
ChatServer\ChatMessage.cs
using System;
using System.Runtime.Serialization;
public class ChatMessage
{
public DateTime Time { get; set; } = DateTime.Now;
public string Author { get; set; } = null!;
public string Content { get; set; } = null!;
}
ChatServer\ChatServer.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
ChatServer\Program.cs
var connection = new ChatConnection();
var cts = new CancellationTokenSource();
// 模拟消息接收(消费者)
var receiveTask = Task.Run(async () =>
{
await foreach (var msg in connection.AsEnumerable(cts.Token))
{
await Task.Delay(1000);
Console.WriteLine($"{msg.Time} {msg.Author}: {msg.Content}");
}
});
// 模拟消息发送(生产者)
connection.OnNext(new ChatMessage { Author = "Alice", Content = "Hello!" });
connection.OnNext(new ChatMessage { Author = "Bob", Content = "Hi Alice!" });
// 结束消息流
connection.OnCompleted();
// 等待接收任务完成
await receiveTask;
ChatServer\run.bat
dotnet run