怪奇物语

怪奇物语

首页 新随笔 联系 管理

消息服务器,生产者和消费者,消息队列

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
posted on 2025-06-15 08:00  超级无敌美少男战士  阅读(19)  评论(0)    收藏  举报