当前位置: 首页 > news >正文

C# .NET多线程异步记录日声,队列LOG

如果你希望以最简单的方式实现,同时保证线程安全和基本的异步支持,可以使用以下简化方案。这个方案直接使用 File.AppendAllTextAsync 并添加锁机制,适合错误情况较少的场景:

简化实现方案csharp

private readonly object _fileLock = new object();
private readonly string _logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "client_close.log");// 移除客户端
public void RemoveClient(string clientId, string errinfo = null)
{if (_clients.TryRemove(clientId, out _) && _lastHeartbeat.TryRemove(clientId, out _)){// 记录日志(异步但顺序执行)_ = LogClientRemovalAsync(clientId, errinfo);}
}// 异步记录客户端移除信息
private async Task LogClientRemovalAsync(string clientId, string errinfo)
{string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff},{clientId},{errinfo ?? "正常关闭"}";// 使用锁确保文件写入的线程安全lock (_fileLock){try{// 异步写入文件(在锁内使用异步方法,确保顺序执行)File.AppendAllTextAsync(_logFilePath, logEntry + Environment.NewLine).Wait();}catch (Exception ex){Console.WriteLine($"日志写入失败: {ex.Message}");// 可添加更复杂的错误处理逻辑}}
}

关键点说明

  1. 线程安全

    • 使用 lock 关键字确保同一时间只有一个线程可以写入文件。
    • 虽然 File.AppendAllTextAsync 是异步方法,但在锁内部调用其同步版本 Wait(),确保写入顺序。
  2. 异步支持

    • 方法标记为 async,但使用 Wait() 同步等待写入完成,避免异步操作的复杂性。
    • 调用方通过 _ = LogClientRemovalAsync(...) 非阻塞地触发日志记录。
  3. 错误处理

    • 简单捕获并打印异常,适合错误情况较少的场景。

日志文件格式

与之前相同:

2023-06-15 14:30:21.123,client123,连接超时
2023-06-15 14:30:35.456,client456,正常关闭

优点

  • 代码简单:无需额外的队列或后台任务。
  • 资源占用少:不使用额外的线程或信号量。
  • 基本异步支持:在锁内使用异步文件操作,减少 IO 阻塞。

注意事项

  • 性能限制:在高并发场景下可能成为瓶颈,因为每次写入都需要获取锁。
  • 异常处理简化:仅简单打印错误,未实现重试或日志缓冲。

如果你的应用程序错误情况确实很少,且并发量不高,这种实现方式足够简单且可靠。


要实现支持异步多线程并发操作的客户端关闭信息记录功能,需要考虑线程安全和文件写入的异步性。以下是一个完整的解决方案:

实现方案

1. 添加异步日志方法

使用 ConcurrentQueue 缓存日志条目,并通过后台任务异步写入文件,确保线程安全和高性能。

private readonly ConcurrentQueue<string> _logQueue = new ConcurrentQueue<string>();
private readonly CancellationTokenSource _logCts = new CancellationTokenSource();
private readonly SemaphoreSlim _fileLock = new SemaphoreSlim(1, 1);
private readonly string _logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "client_close.log");// 初始化时启动日志写入任务
public WebSocketHeartbeatManager()
{// 启动异步日志写入任务_ = Task.Run(() => ProcessLogQueueAsync(_logCts.Token));
}// 异步处理日志队列
private async Task ProcessLogQueueAsync(CancellationToken cancellationToken)
{while (!cancellationToken.IsCancellationRequested){try{if (_logQueue.TryDequeue(out string logEntry)){await WriteLogEntryAsync(logEntry);}else{await Task.Delay(100, cancellationToken); // 短暂等待,避免CPU空转}}catch (OperationCanceledException){// 正常取消,退出循环break;}catch (Exception ex){Console.WriteLine($"日志处理错误: {ex.Message}");// 发生错误时等待更长时间await Task.Delay(1000, cancellationToken);}}
}// 异步写入日志条目
private async Task WriteLogEntryAsync(string logEntry)
{await _fileLock.WaitAsync(); // 获取文件锁try{// 使用File.AppendAllTextAsync确保原子性await File.AppendAllTextAsync(_logFilePath, logEntry + Environment.NewLine);}finally{_fileLock.Release(); // 释放文件锁}
}// 清理资源
public void Dispose()
{_logCts.Cancel();_logCts.Dispose();_fileLock.Dispose();
}
2. 修改 RemoveClient 方法

添加可选参数 errinfo,并将日志信息加入队列:csharp

// 移除客户端
public void RemoveClient(string clientId, string errinfo = null)
{if (_clients.TryRemove(clientId, out _) && _lastHeartbeat.TryRemove(clientId, out DateTime removedTime)){// 格式化日志条目:时间,CLIENTID,errinfostring logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff},{clientId},{errinfo ?? "正常关闭"}";// 将日志条目加入队列(线程安全操作)_logQueue.Enqueue(logEntry);}
}

关键点说明

  1. 线程安全

    • 使用 ConcurrentQueue 存储日志条目,确保多线程环境下的入队操作安全。
    • 通过 SemaphoreSlim 控制文件写入的并发访问,避免文件冲突。
  2. 异步处理

    • 日志写入操作在单独的后台任务中进行,不阻塞主线程。
    • 使用 File.AppendAllTextAsync 实现异步文件写入。
  3. 异常处理

    • 在日志处理循环中捕获并处理可能的异常,确保系统稳定运行。
  4. 资源释放

    • 实现 Dispose 方法,在对象销毁时正确释放 CancellationTokenSource 和 SemaphoreSlim

日志文件格式示例plaintext

2023-06-15 14:30:21.123,client123,连接超时
2023-06-15 14:30:35.456,client456,正常关闭
2023-06-15 14:31:02.789,client789,协议错误

使用示例csharp

// 正常关闭
manager.RemoveClient("client123");// 异常关闭
manager.RemoveClient("client456", "心跳超时");

这种实现方式在高并发场景下依然能保持良好性能,同时确保所有客户端关闭信息都被可靠记录。

http://www.lryc.cn/news/573620.html

相关文章:

  • docker镜像封装与发布微服务学习
  • NotePad++ 怎么没有找到插件管理?
  • Python打卡DAY34
  • 【科研绘图系列】R语言绘制论文组合图形(multiple plots)
  • Redis快的原因
  • 【单调栈】-----【小A的柱状图】
  • 大零售生态下开源链动2+1模式、AI智能名片与S2B2C商城小程序的协同创新研究
  • 如何用AI开发完整的小程序<7>—让AI微调UI排版
  • Spring AI 项目实战(十):Spring Boot + AI + DeepSeek 构建智能合同分析技术实践(附完整源码)
  • opencv 之双目立体标定算法核心实现
  • C#控制Button单击事件指定时间间隔触发
  • 计算鱼眼相机的内参矩阵和畸变系数方法
  • 风险矩阵与灰色综合评价
  • AMAT P5000 CVDFDT CVDMAINT Precision 5000 Mark 操作 电气原理 PCB图 电路图等
  • git 如何忽略某个文件夹文件
  • NW896NW859美光固态闪存NW893NX764
  • 激活函数为何能增强神经网络的非线性表达能力?
  • 【node】Mac m1 安装nvm 和node
  • WEB3合约开发以太坊中货币单位科普
  • 【数据结构与算法】数据结构核心概念系统梳理
  • go excel解析库xuri/excelize中的SAX
  • 【人工智能基础】初识神经网络
  • 2.jupyter切换使用conda虚拟环境的最佳方法
  • Flink SQL Connector Kafka 核心参数全解析与实战指南
  • Windows防火墙指南大全:安全红线与科学替代方案
  • 通俗理解物联网中的APN
  • Vmware WorkStation 17.5 安装 Ubuntu 24.04-LTS Server 版本
  • 【机器学习】数学基础——张量(进阶篇)
  • 九联UNT403G/UNT413G-国科GK6323V100C-2+8G/4+16G-安卓9.0-优盘短接强刷固件包
  • 抖音小程序开发:ttml和传统html的区别