C# - 锁定和 StreamWrite 问题(多线程)

Posted

技术标签:

【中文标题】C# - 锁定和 StreamWrite 问题(多线程)【英文标题】:C# - Issue with lock and StreamWrite (Multi-threading) 【发布时间】:2020-08-28 06:28:29 【问题描述】:

我创建了一个简单的 Logger 类,它使用 StreamWriter 将文本附加到日志文件。主程序是一个任务调度器,它异步执行任务,并在执行成功与否时写入日志文件。 该程序从数据库中读取并将任务添加到列表中,并且 ExecuteAll() 方法异步执行该列表中的每个任务。如果读取\写入数据库或任务执行中的任何步骤出错 - 我使用 Logger.Error()。

当我运行程序时一切正常,但是当我将 Thread.Sleep(5000) 添加到每个任务以模拟连接到 API 时,我得到一个 IOExecption

System.IO.IOException: The process cannot access the file 'Log.txt' because it is being used by another process.

记录器类:

    public class Logger
    
        const string filePath = @"Log.txt";
        private static readonly object writerLock = new object();
        public static string Info(string log)
        
            string logMessage = DateTime.Now.ToString(dateTimeFormat) + " | INFO | " + log;
            lock (writerLock)
            
                using (StreamWriter logToFile = File.AppendText(filePath))
                
                    logToFile.WriteLine(logMessage);
                    Console.WriteLine(logMessage);
                
            
            return logMessage;
        
        public static string Error(string log)
        
            string logMessage = DateTime.Now.ToString(dateTimeFormat) + " | ERROR | " + log;
            lock (writerLock)
            
                using (StreamWriter logToFile = File.AppendText(filePath))
                
                    logToFile.WriteLine(logMessage);
                    Console.WriteLine(logMessage);
                
            
            return logMessage;
        
    

异步方法:

public void ExecuteJob(Job job)
        
            try
            
                Thread.Sleep(5000);
                job.Execute(); // Executes: Log.Info("Job Executed.");
            
            catch(Exception ex)
            
                Logger.Error($"Couldn't Execute Job #job.jobId: " + ex);
            
            finally
            
                DbManager.UpdateJobInTable(job.jobId);
                jobs.Remove(job);

            
        
        //Execute all jobs
        public async void ExecuteAll()
        
            if (!jobs.Any())
                return;
            List<Task> tasks = new List<Task>();
            foreach (Job job in jobs.ToArray())
            
                tasks.Add(
                    Task.Run(() =>
                    
                        ExecuteJob(job); 
                    )
                );
            
            await Task.WhenAll(tasks);
        

当一个线程已经在使用它时,为什么锁不阻止线程进入 StreamWriter?为什么当我在没有 Thread.Sleep(5000) 的情况下运行程序时它似乎工作正常?

希望有人可以提供帮助。提前致谢。

【问题讨论】:

你说错误是“多进程”? @RonD 是否有其他代码(Logger 除外)可以将数据读/写到Log.txt?当错误发生时,您是否能够确保程序的其他实例没有运行? @RodD,无法重现此问题。此代码应该可以工作,请确保您的应用程序只有一个实例正在运行,并且该文件不会被任何其他应用程序或您应用程序中的任何其他代码访问。 为什么要重新发明***?使用现有的ReadWriterLock 支持多线程写入。我也确信多线程日志库也存在。 这段代码没有任何问题(好吧,除了不必要地发明了一个日志系统);锁定是正确的。我的第一个猜测是您的代码正在运行防病毒程序;他们中的一些人喜欢检查刚刚编写的文件。 【参考方案1】:

锁似乎不适用于异步进程:

https://blog.cdemi.io/async-waiting-inside-c-sharp-locks/

lock 关键字只能用于同步同步代码。来自 MSDN:

await 表达式不能出现在同步函数体、查询表达式、lock 语句块或不安全上下文中。

【讨论】:

你不能在锁块中写await ...,但是你可以在异步方法中调用lock,它应该像普通线程一样工作.. @Neil 我尝试按照文章中的说明对信号量进行更改,弹出相同的异常。

以上是关于C# - 锁定和 StreamWrite 问题(多线程)的主要内容,如果未能解决你的问题,请参考以下文章

c# 多线程互斥问题。。

C# 锁定、属性和权限

在多线程应用程序表单上使用while循环锁定c#阻塞单核机器上的所有线程

StreamWrite类

FileStream和StreamReader,StreamWrite,BinaryWriter

C#多线程间的同步问题