在多线程访问读写同一个文件时,经常遇到异常:“文件正在由另一进程使用,因此该进程无法访问此文件”。

多线程访问统一资源的异常,

解决方案1,保证读写操作单线程执行,可以使用lock

解决方案2,使用System.Threading.ReaderWriterLockSlim ,对读写操作锁定处理

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

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

调用 EnterWriteLock 方法 进入写入状态,在调用线程进入锁定状态之前一直处于阻塞状态,因此可能永远都不返回
调用 TryEnterWriteLock 方法 进入写入状态,可指定阻塞的间隔时间,如果调用线程在此间隔期间并未进入写入模式,将返回false
调用 ExitWriteLock 方法 退出写入状态,应使用 finally 块执行 ExitWriteLock 方法,从而确保调用方退出写入模式。

一、不是用锁处理,多线程访问文件不定时抛出异常

static void Main(string[] args)
{
//迭代运行写入日志记录,由于多个线程同时写入同一个文件将会导致错误
Parallel.For(, LogCount, e =>
{
WriteLog();
});
Console.Read();
}
static int LogCount = ;
static int FailedCount = ;
static int WriteCount = ;
static void WriteLog()
{
try
{
WriteCount++; LogHelper.LogHelper _log = new LogHelper.LogHelper("g:\\temp2\\one.txt", true);
DateTime now = DateTime.Now;
var logContent = string.Format("Tid: {0}{1} {2}=>{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(), now.ToLongDateString(), now.ToLongTimeString(), WriteCount);
_log.WriteLine(logContent);
}
catch (Exception ex)
{
FailedCount++;
Console.WriteLine("累计出错数:" + FailedCount);
Console.WriteLine(ex.Message);
}
}

二、使用读写锁 同步写入文件处理

//读写锁,当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入
static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
static void WriteLog()
{
try
{
//设置读写锁为写入模式独占资源,其他写入请求需要等待本次写入结束之后才能继续写入
//注意:长时间持有读线程锁或写线程锁会使其他线程发生饥饿 (starve)。 为了得到最好的性能,需要考虑重新构造应用程序以将写访问的持续时间减少到最小。
//从性能方面考虑,请求进入写入模式应该紧跟文件操作之前,在此处进入写入模式仅是为了降低代码复杂度
//因进入与退出写入模式应在同一个try finally语句块内,所以在请求进入写入模式之前不能触发异常,否则释放次数大于请求次数将会触发异常
LogWriteLock.EnterWriteLock(); WriteCount++;
LogHelper.LogHelper _log = new LogHelper.LogHelper("g:\\temp2\\one.txt", true);
DateTime now = DateTime.Now;
var logContent = string.Format("Tid: {0}{1} {2}=>{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(), now.ToLongDateString(), now.ToLongTimeString(), WriteCount);
_log.WriteLine(logContent);
}
catch (Exception ex)
{
FailedCount++;
Console.WriteLine("累计出错数:" + FailedCount);
Console.WriteLine(ex.Message);
}
finally
{
//退出写入模式,释放资源占用
//注意:一次请求对应一次释放
//若释放次数大于请求次数将会触发异常[写入锁定未经保持即被释放]
//若请求处理完成后未释放将会触发异常[此模式不下允许以递归方式获取写入锁定]
LogWriteLock.ExitWriteLock();
}
}

三、补充:初始化FileStream时使用包含文件共享属性(System.IO.FileShare)的构造函数比使用自定义线程锁更为安全高效

      class Program
{
static int LogCount = ;
static int WritedCount = ;
static int FailedCount = ; static void Main(string[] args)
{
//迭代运行写入日志记录
Parallel.For(, LogCount, e =>
{
WriteLog();
}); Console.WriteLine(string.Format("\r\nLog Count:{0}.\t\tWrited 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}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString()); var logContentBytes = Encoding.Default.GetBytes(logContent);
//由于设置了文件共享模式为允许随后写入,所以即使多个线程同时写入文件,也会等待之前的线程写入结束之后再执行,而不会出现错误
using (FileStream logFile = new FileStream(logFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
{
logFile.Seek(, SeekOrigin.End);
logFile.Write(logContentBytes, , logContentBytes.Length);
} WritedCount++;
}
catch (Exception ex)
{
FailedCount++;
Console.WriteLine(ex.Message);
}
}
}

更多:

C# 获取当前路径方法整理

C#获取当前系统磁盘符、系统目录、桌面等

C#获取磁盘列表与信息

C#多线程读写同一文件处理的更多相关文章

  1. JAVA多线程读写文件范例

    在写之前先声明,本文是基于之前在博客园网站上检索到的一份JAVA多线程读写文件的示例,我在写自己的程序时是在那位作者写的基础上做了改良,但已不记得原文的地址.如果有知情者,烦请帖出地址,我在此文上加入 ...

  2. C#使用读写锁三行代码简单解决多线程并发写入文件时线程同步的问题

    (补充:初始化FileStream时使用包含文件共享属性(System.IO.FileShare)的构造函数比使用自定义线程锁更为安全和高效,更多内容可点击参阅) 在开发程序的过程中,难免少不了写入错 ...

  3. SQLite多线程读写实践及常见问题总结

    多线程读写 SQLite实质上是将数据写入一个文件,通常情况下,在应用的包名下面都能找到xxx.db的文件,拥有root权限的手机,可以通过adb shell,看到data/data/packagen ...

  4. 为什么多线程读写 shared_ptr 要加锁?

    https://www.cnblogs.com/Solstice/archive/2013/01/28/2879366.html 为什么多线程读写 shared_ptr 要加锁? 陈硕(giantch ...

  5. [转载]C#读写txt文件的两种方法介绍

    C#读写txt文件的两种方法介绍 by 大龙哥 1.添加命名空间 System.IO; System.Text; 2.文件的读取 (1).使用FileStream类进行文件的读取,并将它转换成char ...

  6. 用opencsv文件读写CSV文件

    首先明白csv文件长啥样儿: 用excel打开就变成表格了,看不到细节 推荐用其它简单粗暴一点儿的编辑器,比如Notepad++, csv文件内容如下: csv文件默认用逗号分隔各列. 有了基础的了解 ...

  7. 在.net中读写config文件的各种方法

    阅读目录 开始 config文件 - 自定义配置节点 config文件 - Property config文件 - Element config文件 - CDATA config文件 - Collec ...

  8. MFC vs2012 Office2013 读写excel文件

    近期在忙一个小项目(和同学一起搞的),在这里客户要求不但读写txt,而且可以读写excel文件,这里本以为很简单,结果...废话少说,过程如下: 笔者环境:win7 64+VS2012+Office2 ...

  9. Java读写资源文件类Properties

    Java中读写资源文件最重要的类是Properties 1) 资源文件要求如下: 1.properties文件是一个文本文件 2.properties文件的语法有两种,一种是注释,一种属性配置.  注 ...

随机推荐

  1. Git(五)IDEA应用Git

    一.IDEA客户端git 1.提交代码到本地仓库 1. 关联Git,创建本地库 关联git 配置git环境变量 设置本地仓库目录,一般是IDEA工作空间,选择VCS->Import into V ...

  2. 【BZOJ】3168: [Heoi2013]钙铁锌硒维生素

    题解 Ca Fe Zn Se 显然我们既然初始矩阵就能通过线性变换变成单位矩阵,则该矩阵一定有逆 没有逆输出NIE 而且因为这些向量两两正交,则表示一个向量的时候表示方法唯一 那么我们求一个逆可以求出 ...

  3. Storm目录树和任务提交过程

    Storm组件本地目录树 Storm zookeeper目录树 Storm任务提交的过程

  4. springBoot事物

    1.事物 只是需要一个注解即可 2.事物程序 package com.caojun.springboot; import org.springframework.beans.factory.annot ...

  5. linux中没有dos2UNIX或者UNIX2dos命令解决办法

    安装方法: 在http://linux.softpedia.com/progDownload/Dos2Unix-Download-5519.html下载hd2u-1.0.0.tgz [root@loc ...

  6. vuex使用modules namespaced 后,模块名不同,函数名相同时候在组件中分发Action

    你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用 ...

  7. 关于我学XSS躺过的那些坑

    XSS字符编码 在学习编码绕过时由于数量多,类型相似,不太容易记得住,记得全,故做此记录. 0x01 Html标签属性中执行 简单了解: Html标签属性中的XSS问题多属于javascript伪协议 ...

  8. iOS技术篇:sizeToFit 和 sizeThatFits 区别

    sizeToFit:会计算出最优的 size 而且会改变自己的size UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(, , , ...

  9. Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem K. UTF-8 Decoder 模拟题

    Problem K. UTF-8 Decoder 题目连接: http://opentrains.snarknews.info/~ejudge/team.cgi?SID=c75360ed7f2c702 ...

  10. ROS知识(23)——行为树Behavio Tree原理

    机器人的复杂行为的控制结构CA(Contrl Architecture)通常使用有限状态机来实现,例如ROS提供的smach.行为树是另外一种实现机器人控制的方法,ROS下代表的开源库有pi_tree ...