C#使用读写锁三行代码简单解决多线程并发的问题

2019-12-30 15:11:20王旭

在开发程序的过程中,难免少不了写入错误日志这个关键功能。实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件。

选择最后一种方法实现的时候,若对文件操作与线程同步不熟悉,问题就有可能出现了,因为同一个文件并不允许多个线程同时写入,否则会提示“文件正在由另一进程使用,因此该进程无法访问此文件”。

这是文件的并发写入问题,就需要用到线程同步。而微软也给线程同步提供了一些相关的类可以达到这样的目的,本文使用到的 System.Threading.ReaderWriterLockSlim 便是其中之一。

该类用于管理资源访问的锁定状态,可实现多线程读取或进行独占式写入访问。利用这个类,我们就可以避免在同一时间段内多线程同时写入一个文件而导致的并发写入问题。

读写锁是以 ReaderWriterLockSlim 对象作为锁管理资源的,不同的 ReaderWriterLockSlim 对象中锁定同一个文件也会被视为不同的锁进行管理,这种差异可能会再次导致文件的并发写入问题,所以 ReaderWriterLockSlim 应尽量定义为只读的静态对象。

ReaderWriterLockSlim 有几个关键的方法,本文仅讨论写入锁:

调用 EnterWriteLock 方法 进入写入状态,在调用线程进入锁定状态之前一直处于阻塞状态,因此可能永远都不返回。

调用 TryEnterWriteLock 方法 进入写入状态,可指定阻塞的间隔时间,如果调用线程在此间隔期间并未进入写入模式,将返回false。

调用 ExitWriteLock 方法 退出写入状态,应使用 finally 块执行 ExitWriteLock 方法,从而确保调用方退出写入模式。

Don't talk, show me the code.

1.多线程同时写入文件


class Program
 {
 static int LogCount = 100;
 static int WritedCount = 0;
 static int FailedCount = 0;
 static void Main(string[] args)
 {
 //迭代运行写入日志记录,由于多个线程同时写入同一个文件将会导致错误
 Parallel.For(0, LogCount, e =>
 {
 WriteLog();
 });
 Console.WriteLine(string.Format("rnLog Count:{0}.ttWrited Count:{1}.tFailed Count:{2}.", LogCount.ToString(), WritedCount.ToString(), FailedCount.ToString()));
 Console.Read();
 }
 static void WriteLog()
 {
 try
 {
 var logFilePath = "log.txt";
 var now = DateTime.Now;
 var logContent = string.Format("Tid: {0}{1} {2}.{3}rn", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString());
 File.AppendAllText(logFilePath, logContent);
 WritedCount++;
 }
 catch (Exception ex)
 {
 FailedCount++;
 Console.WriteLine(ex.Message);
 }
 }
 }

运行结果:

C#,读写锁,多线程