导读:本期聚焦于小伙伴创作的《C#如何将日志先写入内存队列再批量写入文件实现异步批处理》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#如何将日志先写入内存队列再批量写入文件实现异步批处理》有用,将其分享出去将是对创作者最好的鼓励。

在C#应用开发中,日志记录是排查问题、监控系统状态的重要手段,但传统的同步逐条写入文件的方式,在高并发场景下会频繁触发磁盘IO操作,不仅会降低日志写入效率,还可能阻塞业务线程,影响应用的整体性能。采用先写入内存队列再批量写入文件的异步批处理方案,可以有效减少磁盘操作次数,提升日志处理的吞吐量。

C#如何将日志先写入内存队列再批量写入文件实现异步批处理

核心实现思路

整个方案的核心分为三个部分:内存日志队列、日志生产端、日志消费端。生产端负责将日志信息写入内存队列,消费端异步从队列中取出批量日志,统一写入文件,两者通过线程安全的内存队列解耦,避免直接操作文件带来的性能问题。

内存队列选型

内存队列需要支持多线程并发写入,同时要保证消费端可以安全地批量取出元素,这里选择ConcurrentQueue<string>作为日志队列容器,它是.NET提供的线程安全队列,无需额外加锁即可支持多线程操作。

日志生产端实现

生产端只需要将格式化后的日志字符串放入内存队列即可,操作非常轻量,不会阻塞业务线程。示例代码如下:

using System;
using System.Collections.Concurrent;
using System.Threading;

public class LogProducer
{
    // 全局共享的日志内存队列
    private static readonly ConcurrentQueue<string> _logQueue = new ConcurrentQueue<string>();
    // 批处理阈值,队列中日志数量达到该值就触发写入
    private static readonly int _batchThreshold = 10;
    // 定时器,用于定期触发批处理写入,避免队列中日志长时间不写入
    private static readonly Timer _batchTimer;
    // 定时器触发间隔,单位毫秒
    private static readonly int _timerInterval = 2000;

    static LogProducer()
    {
        // 初始化定时器,回调方法为批处理写入逻辑
        _batchTimer = new Timer(FlushLogBatch, null, _timerInterval, _timerInterval);
    }

    /// <summary>
    /// 写入日志到内存队列
    /// </summary>
    /// <param name="logContent">日志内容</param>
    public static void WriteLog(string logContent)
    {
        // 格式化日志,添加时间戳
        string formattedLog = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {logContent}";
        // 放入内存队列
        _logQueue.Enqueue(formattedLog);
        // 如果队列长度达到批处理阈值,主动触发一次批处理
        if (_logQueue.Count >= _batchThreshold)
        {
            FlushLogBatch(null);
        }
    }

    /// <summary>
    /// 批量刷新日志到文件
    /// </summary>
    private static void FlushLogBatch(object state)
    {
        // 如果队列为空,直接返回
        if (_logQueue.IsEmpty)
        {
            return;
        }
        // 临时存储待写入的日志集合
        var logBatch = new System.Collections.Generic.List<string>();
        // 从队列中取出所有日志,最多取100条避免单次写入过多
        int takeCount = 0;
        while (_logQueue.TryDequeue(out string log) && takeCount < 100)
        {
            logBatch.Add(log);
            takeCount++;
        }
        // 如果有日志需要写入,调用文件写入方法
        if (logBatch.Count > 0)
        {
            LogFileWriter.WriteLogsToFile(logBatch);
        }
    }
}

日志消费端实现

消费端负责将批量日志写入文件,这里需要注意文件写入的线程安全,避免多个线程同时操作同一个日志文件导致内容错乱。示例代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

public class LogFileWriter
{
    // 日志文件存放目录
    private static readonly string _logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
    // 写入文件时的锁对象,保证同一时间只有一个线程写入文件
    private static readonly object _fileWriteLock = new object();

    static LogFileWriter()
    {
        // 如果日志目录不存在,自动创建
        if (!Directory.Exists(_logDirectory))
        {
            Directory.CreateDirectory(_logDirectory);
        }
    }

    /// <summary>
    /// 批量写入日志到文件
    /// </summary>
    /// <param name="logs">待写入的日志集合</param>
    public static void WriteLogsToFile(List<string> logs)
    {
        // 按日期生成日志文件名,避免单个文件过大
        string logFileName = $"log_{DateTime.Now:yyyyMMdd}.txt";
        string logFilePath = Path.Combine(_logDirectory, logFileName);
        // 拼接日志内容,每个日志占一行
        StringBuilder logContentBuilder = new StringBuilder();
        foreach (var log in logs)
        {
            logContentBuilder.AppendLine(log);
        }
        // 加锁写入文件,保证线程安全
        lock (_fileWriteLock)
        {
            // 使用追加模式写入,编码为UTF8
            File.AppendAllText(logFilePath, logContentBuilder.ToString(), Encoding.UTF8);
        }
    }
}

使用示例

在业务代码中调用日志写入方法非常简单,不需要关心批处理和文件写入的细节:

class Program
{
    static void Main(string[] args)
    {
        // 模拟高并发场景下的日志写入
        for (int i = 0; i < 50; i++)
        {
            int logIndex = i;
            // 启动多个线程模拟并发写入
            System.Threading.Tasks.Task.Run(() =>
            {
                LogProducer.WriteLog($"这是第{logIndex}条测试日志,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
            });
        }
        // 等待所有日志处理完成
        System.Threading.Thread.Sleep(5000);
        Console.WriteLine("日志写入完成");
    }
}

注意事项

  • 内存队列的大小需要合理控制,如果应用产生日志的速度远快于消费端写入速度,队列会持续膨胀占用内存,可以增加批处理阈值或者优化文件写入性能。
  • 定时器触发间隔和批处理阈值需要根据实际业务场景调整,平衡日志实时性和性能。
  • 文件写入时需要处理异常,比如磁盘空间不足、文件被占用等情况,避免异常导致应用崩溃,可以在WriteLogsToFile方法中添加try-catch逻辑。
  • 如果应用需要重启,内存队列中未写入的日志会丢失,如果需要保证日志不丢失,可以在应用关闭前主动调用一次FlushLogBatch方法,将队列中剩余日志全部写入文件。

C#异步批处理内存队列文件日志日志写入修改时间:2026-07-17 22:15:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。