You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#日志写入遇文件被占用异常,求线程锁或队列实现方案

解决方案:线程锁与等待队列实现无冲突日志写入

一、线程锁方式(简单直接,适合低并发场景)

问题根源是多线程同时访问同一个日志文件,用lock关键字可确保同一时间只有一个线程写入文件。锁对象必须声明为静态且只读,避免不同实例创建不同锁导致同步失效。

修改后的代码示例:

// 静态只读锁对象,确保所有线程共享同一锁实例
private static readonly object _logLock = new object();

public void WriteLog(string webService, string eventName, string transactionId, string debuglog)
{
    DateTime time = DateTime.Now;
    time = time.Minute > 30 ? time.Date.AddHours(time.Hour).AddMinutes(30) : time.Date.AddHours(time.Hour).AddMinutes(0);

    var filePath = $"C:/temp/DebugLog/{webService}/{time:yyyyMMddHHmss}_Logs/";
    var fullFileName = Path.Combine(filePath, $"{time:yyyyMMddHHmss}.txt");

    if (!Directory.Exists(filePath))
    {
        Directory.CreateDirectory(filePath);
    }

    // 加锁后执行文件写入,避免多线程冲突
    lock (_logLock)
    {
        try
        {
            using (StreamWriter w = File.AppendText(fullFileName))
            {
                w.WriteLine($"{Guid.NewGuid():N}; {DateTime.Now:yyyyMMddHHmssms}; {webService}; {eventName}; {DateTime.Now}; {transactionId}; {debuglog}");
                // using块会自动释放StreamWriter资源,无需手动调用Close()
            }
        }
        catch (Exception ex)
        {
            // 可在此添加异常处理逻辑,比如记录到系统事件日志
            // EventLog.WriteEntry("日志写入失败", ex.Message, EventLogEntryType.Error);
        }
    }
}

核心注意点:

  • 锁对象必须是静态的,否则不同类实例会生成独立锁,无法起到同步作用
  • 使用Path.Combine拼接路径,避免手动拼接导致的分隔符错误
  • using块自动管理资源生命周期,无需手动关闭流

二、等待队列方式(适合高并发场景,异步解耦)

高并发场景下,线程锁会导致线程阻塞等待,影响业务性能。可采用生产-消费模式:用线程安全队列缓存日志内容,单独启一个线程负责写入文件,实现业务逻辑与日志操作解耦。

示例代码:

// 线程安全的日志队列,用于缓存待写入的日志数据
private static readonly ConcurrentQueue<string> _logQueue = new ConcurrentQueue<string>();
// 标记队列处理线程的运行状态,避免重复启动
private static bool _isProcessingQueue = false;
// 控制队列处理线程启动的锁对象
private static readonly object _queueLock = new object();

public void WriteLog(string webService, string eventName, string transactionId, string debuglog)
{
    DateTime time = DateTime.Now;
    time = time.Minute > 30 ? time.Date.AddHours(time.Hour).AddMinutes(30) : time.Date.AddHours(time.Hour).AddMinutes(0);

    var logContent = $"{Guid.NewGuid():N}; {DateTime.Now:yyyyMMddHHmssms}; {webService}; {eventName}; {DateTime.Now}; {transactionId}; {debuglog}";
    var filePath = $"C:/temp/DebugLog/{webService}/{time:yyyyMMddHHmss}_Logs/";
    var fullFileName = Path.Combine(filePath, $"{time:yyyyMMddHHmss}.txt");

    // 将日志内容和目标文件路径序列化后入队
    _logQueue.Enqueue(JsonConvert.SerializeObject(new { FilePath = fullFileName, Content = logContent }));

    // 确保仅启动一个队列处理线程
    lock (_queueLock)
    {
        if (!_isProcessingQueue)
        {
            _isProcessingQueue = true;
            Task.Run(() => ProcessLogQueue());
        }
    }
}

private void ProcessLogQueue()
{
    while (_logQueue.TryDequeue(out string logItem))
    {
        try
        {
            var logData = JsonConvert.DeserializeObject<dynamic>(logItem);
            string fullFileName = logData.FilePath;
            string content = logData.Content;

            var directory = Path.GetDirectoryName(fullFileName);
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            // 写入文件时可针对单个文件加锁,避免同一文件的并发写入冲突
            lock (_logLock)
            {
                using (StreamWriter w = File.AppendText(fullFileName))
                {
                    w.WriteLine(content);
                }
            }
        }
        catch (Exception ex)
        {
            // 异常处理:可将失败的日志重新入队重试,或记录到错误日志
            // _logQueue.Enqueue(logItem);
        }
    }

    // 队列处理完毕,重置运行状态标记
    lock (_queueLock)
    {
        _isProcessingQueue = false;
    }
}

核心注意点:

  • 用ConcurrentQueue实现线程安全的队列操作,无需手动加锁
  • 消费线程仅启动一个,避免资源浪费
  • 支持失败日志重试,提升日志写入可靠性

总结

  • 低并发场景:优先使用线程锁,实现简单、性能开销小
  • 高并发场景:采用等待队列+异步消费,解耦业务与日志操作,避免阻塞业务线程

内容的提问来源于stack exchange,提问作者Ah Chua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 11:40:33