C#编程之读写锁三句代码简单解决“文件正在由另一进程使用,因此该进程无法访问此文件”
小标 2018-12-28 来源 : 阅读 2391 评论 0

摘要:本文主要向大家介绍了C#编程之读写锁三句代码简单解决“文件正在由另一进程使用,因此该进程无法访问此文件”,通过具体的内容向大家展示,希望对大家学习C#编程有所帮助。

本文主要向大家介绍了C#编程之读写锁三句代码简单解决“文件正在由另一进程使用,因此该进程无法访问此文件”,通过具体的内容向大家展示,希望对大家学习C#编程有所帮助。

在开发程序的过程中,难免少不了写入错误日志这个关键功能。实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件。
选择最后一种方法实现的时候,若对文件操作与线程同步不熟悉,问题就有可能出现了,因为同一个文件并不允许多个线程同时写入,否则会提示“文件正在由另一进程使用,因此该进程无法访问此文件”。
 
这是文件的并发写入问题,就需要用到线程同步。而微软也给进程同步提供了一些相关的类可以达到这样的目的,本文使用到的 System.Threading.ReaderWriterLockSlim 便是其中之一。
该类用于管理资源访问的锁定状态,可实现多线程读取或进行独占式写入访问。利用这个类,我们就可以避免在同一时间段多线程同时写入一个文件而导致的并发写入问题。
 
读写锁是以 ReaderWriterLockSlim 对象作为管理资源的,不同的 ReaderWriterLockSlim 对象中锁定同一个文件也会被视为不同的锁进行管理,这种差异可能会再次导致文件的并发写入问题,所以 ReaderWriterLockSlim 应尽量定义为只读的静态对象。
 
ReaderWriterLockSlim 有几个关键的方法,本文仅讨论写入锁:
调用 EnterWriteLock 方法 进入写入状态,在调用线程进入锁定状态之前一直处于阻塞状态,因此可能永远都不返回。调用 TryEnterWriteLock 方法 进入写入状态,可指定阻塞的间隔时间,如果调用线程在此间隔期间并未进入写入模式,将返回false。调用 ExitWriteLock 方法 退出写入状态,应使用 finally 块执行 ExitWriteLock 方法,从而确保调用方退出写入模式。
 
Don‘t talk, show me the code.
 
1.多线程同时写入文件

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             int logCount = 100;
 6             int writedCount = 0;
 7             int failedCount = 0;
 8             //迭代运行写入日志记录,由于多个线程同时写入同一个文件将会导致错误
 9             Parallel.For(0, logCount, e =>
10             {
11                 try
12                 {
13                     var logFilePath = "log.txt";
14                     var now = DateTime.Now;
15                     var logContent = string.Format("Tid: {0}{1} {2}.{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString());
16                     File.AppendAllText(logFilePath, logContent);
17                     writedCount++;
18                 }
19                 catch (Exception ex)
20                 {
21                     failedCount++;
22                     Console.WriteLine(ex.Message);
23                 }
24             });
25 
26             Console.WriteLine(string.Format("\r\nLog Count:{0}.\t\tWrited Count:{1}.\tFailed Count:{2}.", logCount.ToString(), writedCount.ToString(), failedCount.ToString()));
27             Console.Read();
28         }
29     }

运行结果:
不使用读写锁,只有部分日志成功写入了日志文件。
 
2.多线程使用读写锁同步写入文件

 1     class Program
 2     {
 3         //读写锁,当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入
 4         static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
 5         static void Main(string[] args)
 6         {
 7             int logCount = 100;
 8             int writedCount = 0;
 9             int failedCount = 0;
10             //迭代运行写入日志记录
11             Parallel.For(0, logCount, e =>
12             {
13                 try
14                 {
15                     var logFilePath = "log.txt";
16                     var now = DateTime.Now;
17                     var logContent = string.Format("Tid: {0}{1} {2}.{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString());
18 
19                     //设置读写锁为写入模式独占资源,其他写入请求需要等待本次写入结束之后才能继续写入
20                     LogWriteLock.EnterWriteLock();
21 
22                     File.AppendAllText(logFilePath, logContent);
23                     writedCount++;
24                 }
25                 catch (Exception ex)
26                 {
27                     failedCount++;
28                     Console.WriteLine(ex.Message);
29                 }
30                 finally
31                 {
32                     //退出写入模式,释放资源占用
33                     LogWriteLock.ExitWriteLock();
34                 }
35             });
36 
37             Console.WriteLine(string.Format("\r\nLog Count:{0}.\t\tWrited Count:{1}.\tFailed Count:{2}.", logCount.ToString(), writedCount.ToString(), failedCount.ToString()));
38             Console.Read();
39         }
40     }

运行结果:
使用读写锁,全部日志成功写入了日志文件。
 
3.复杂多线程环境下使用读写锁同步写入文件

 1     class Program
 2     {
 3         //读写锁,当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入
 4         static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
 5 
 6         static int LogCount = 1000;
 7         static int SumLogCount = 0;
 8         static int WritedCount = 0;
 9         static int FailedCount = 0;
10 
11         static void Main(string[] args)
12         {
13             //往线程池里添加一个任务,迭代写入N个日志
14             SumLogCount += LogCount;
15             ThreadPool.QueueUserWorkItem((obj) =>
16             {
17                 Parallel.For(0, LogCount, e =>
18                 {
19                     WriteLog();
20                 });
21             });
22 
23             //在新的线程里,添加N个写入日志的任务到线程池
24             SumLogCount += LogCount;
25             var thread1 = new Thread(() =>
26             {
27                 Parallel.For(0, LogCount, e =>
28                 {
29                     ThreadPool.QueueUserWorkItem((subObj) =>
30                     {
31                         WriteLog();
32                     });
33                 });
34             });
35             thread1.IsBackground = false;
36             thread1.Start();
37 
38             //添加N个写入日志的任务到线程池
39             SumLogCount += LogCount;
40             Parallel.For(0, LogCount, e =>
41             {
42                 ThreadPool.QueueUserWorkItem((obj) =>
43                 {
44                     WriteLog();
45                 });
46             });
47 
48             //在新的线程里,迭代写入N个日志
49             SumLogCount += LogCount;
50             var thread2 = new Thread(() =>
51             {
52                 Parallel.For(0, LogCount, e =>
53                 {
54                     WriteLog();
55                 });
56             });
57             thread2.IsBackground = false;
58             thread2.Start();
59 
60             //在当前线程里,迭代写入N个日志
61             SumLogCount += LogCount;
62             Parallel.For(0, LogCount, e =>
63             {
64                 WriteLog();
65             });
66 
67             Console.WriteLine("Main Thread Processed.\r\n");
68             while (true)
69             {
70                 Console.WriteLine(string.Format("Sum Log Count:{0}.\t\tWrited Count:{1}.\tFailed Count:{2}.", SumLogCount.ToString(), WritedCount.ToString(), FailedCount.ToString()));
71                 Console.ReadLine();
72             }
73         }
74 
75         static void WriteLog()
76         {
77             try
78             {
79                 var logFilePath = "log.txt";
80                 var now = DateTime.Now;
81                 var logContent = string.Format("Tid: {0}{1} {2}.{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString());
82 
83                 //设置读写锁为写入模式独占资源,其他写入请求需要等待本次写入结束之后才能继续写入
84                 LogWriteLock.EnterWriteLock();
85 
86                 File.AppendAllText(logFilePath, logContent);
87                 WritedCount++;
88             }
89             catch
90             {
91                 FailedCount++;
92             }
93             finally
94             {
95                 //退出写入模式,释放资源占用
96                 LogWriteLock.ExitWriteLock();
97             }
98         }
99     }

运行结果:

部分日志文件内容:

 1 ...
 2 Tid: 36  2016年12月11日 15:29:22.825
 3 Tid: 29  2016年12月11日 15:29:22.830
 4 Tid: 6   2016年12月11日 15:29:22.838
 5 Tid: 26  2016年12月11日 15:29:22.845
 6 Tid: 34  2016年12月11日 15:29:22.854
 7 Tid: 24  2016年12月11日 15:29:22.863
 8 Tid: 27  2016年12月11日 15:29:22.872
 9 Tid: 14  2016年12月11日 15:29:22.877
10 Tid: 23  2016年12月11日 15:29:22.886
11 Tid: 20  2016年12月11日 15:29:22.892
12 Tid: 30  2016年12月11日 15:29:22.898
13 Tid: 9   2016年12月11日 15:29:22.904
14 Tid: 21  2016年12月11日 15:29:22.909
15 Tid: 22  2016年12月11日 15:29:22.915
16 Tid: 7   2016年12月11日 15:29:22.920
17 Tid: 3   2016年12月11日 15:29:22.925
18 Tid: 12  2016年12月11日 15:29:22.931
19 Tid: 5   2016年12月11日 15:29:22.937
20 Tid: 13  2016年12月11日 15:29:22.942
21 Tid: 11  2016年12月11日 15:29:22.947
22 Tid: 19  2016年12月11日 15:29:22.953
23 Tid: 37  2016年12月11日 15:29:22.958
24 Tid: 37  2016年12月11日 15:29:22.964
25 Tid: 40  2016年12月11日 15:29:22.970
26 Tid: 40  2016年12月11日 15:29:22.975
27 Tid: 40  2016年12月11日 15:29:22.980
28 Tid: 40  2016年12月11日 15:29:22.985
29 Tid: 40  2016年12月11日 15:29:22.991
30 Tid: 40  2016年12月11日 15:29:22.997
31 Tid: 31  2016年12月11日 15:29:23.3
32 Tid: 31  2016年12月11日 15:29:23.9
33 Tid: 31  2016年12月11日 15:29:23.14
34 Tid: 31  2016年12月11日 15:29:23.20
35 Tid: 31  2016年12月11日 15:29:23.27
36 Tid: 31  2016年12月11日 15:29:23.33
37 Tid: 31  2016年12月11日 15:29:23.38
38 Tid: 31  2016年12月11日 15:29:23.44
39 Tid: 31  2016年12月11日 15:29:23.49
40 Tid: 31  2016年12月11日 15:29:23.57
41 Tid: 31  2016年12月11日 15:29:23.63
42 Tid: 31  2016年12月11日 15:29:23.68
43 Tid: 31  2016年12月11日 15:29:23.74
44 Tid: 16  2016年12月11日 15:29:23.80
45 Tid: 16  2016年12月11日 15:29:23.86
46 Tid: 16  2016年12月11日 15:29:23.93
47 Tid: 16  2016年12月11日 15:29:23.99
48 Tid: 16  2016年12月11日 15:29:23.105
49 Tid: 16  2016年12月11日 15:29:23.110
50 Tid: 16  2016年12月11日 15:29:23.116
51 Tid: 38  2016年12月11日 15:29:23.122
52 Tid: 38  2016年12月11日 15:29:23.128
53 Tid: 28  2016年12月11日 15:29:23.134
54 Tid: 19  2016年12月11日 15:29:23.139
55 Tid: 25  2016年12月11日 15:29:23.146
56 Tid: 37  2016年12月11日 15:29:23.152
57 Tid: 39  2016年12月11日 15:29:23.158
58 Tid: 32  2016年12月11日 15:29:23.164
59 Tid: 33  2016年12月11日 15:29:23.170
60 Tid: 31  2016年12月11日 15:29:23.176
61 Tid: 35  2016年12月11日 15:29:23.182
62 Tid: 40  2016年12月11日 15:29:23.189
63 Tid: 15  2016年12月11日 15:29:23.194
64 Tid: 18  2016年12月11日 15:29:23.202
65 Tid: 17  2016年12月11日 15:29:23.208
66 Tid: 10  2016年12月11日 15:29:23.215
67 Tid: 16  2016年12月11日 15:29:23.221

复杂多线程环境下使用读写锁,全部日志成功写入了日志文件,由ThreadId和DateTime可以看出是由不同的线程同步写入。
本文旨在提供简单的方式解决文件并发写入的问题,需要深入了解线程同步及高性能文件读写相关知识,请参阅其他相关资料。

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C#.NET频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程