FileShare枚举的使用小结(文件读写锁)

开发过程中,我们往往需要大量与文件交互,读文件,写文件已成家常便饭,本地运行完美,但一上到投产环境,往往会出现很多令人措手不及的意外,或开发中的烦恼,因此,我对普通的C#文件操作做了一次总结,问题大部分如下:

1:写入一些内容到某个文件中,在另一个进程/线程/后续操作中要读取文件内容的时候报异常,提示 System.IO.IOException: 文件“XXX”正由另一进程使用,因此该进程无法访问此文件。

2:在对一个文件进行一些操作后(读/写),随后想追加依然报System.IO.IOException: 文件“XXX”正由另一进程使用,因此该进程无法访问此文件。次问题与1相似。

3:对一个文件进行一些操作后,想删除文件,依然报System.IO.IOException: 文件“XXX”正由另一进程使用,因此该进程无法访问此文件。

看到这些,有经验的同学应该就会说资源没被释放掉,但也存在如下可能性。我们对文件的操作非常频繁,所以写了特定的操作类/组件来维护文件之间的操作,知道特定的时刻才结束,常见的如日志,随着程序的启动便开始写日志,直到程序关闭。但其中也存在我们需要提供一个特殊的操作(读/写/删除)来操作文件,例如我们需要提供一个日志查看器来查看当前日志或所有日志,这时,便无可避免的发生了以上的问题。


代码如下:

static void WriteFile(FileMode fileMode, FileAccess fileAccess, FileShare fileShare)
{
    Console.WriteLine("please input your content.");
    var content = Console.ReadLine();
    FileStream fs = new FileStream(FILEPATH, fileMode, fileAccess, fileShare);
    var buffer = Encoding.Default.GetBytes(content);
    fs.Write(buffer, 0, buffer.Length);
    fs.Flush();
}

首先,我声明了一个写文件方法,并调用它,它将我输入的内容写入指定的文件当中。


代码如下:

WriteFile(FileMode.Create, FileAccess.Write, FileShare.Read);
Console.ReadKey();

但是,在写文件操作结束之后,我并没有释放掉文件流的资源。所以,此时会对文件造成一个锁。我尝试在windows中删除它。

很明显我无法删除掉这个文件,接下来,我尝试读取它。


代码如下:

static void ReadFile(FileAccess fileAccess, FileShare fileShare)
{
    FileStream fs = new FileStream(FILEPATH, FileMode.Open, fileAccess, fileShare);
    var buffer = new byte[fs.Length];
    fs.Position = 0;
    fs.Read(buffer, 0, buffer.Length);
    Console.WriteLine(Encoding.Default.GetString(buffer));
}

我实现了一个读文件方法,并调用了它。


代码如下:

WriteFile(FileMode.Create, FileAccess.Write, FileShare.Read);
ReadFile(FileAccess.Read, FileShare.Read);

一切都很简单,访问模式为只读,这样应该就不会与上面的写锁进行冲突!

但是,结果并非我们所预想的那样,为什么会提示无法访问?回想一下,在前面,我用windows的记事本打开了这个文件,并没有提示说文件被锁定,我也的确能访问,那为何到了程序里就无法访问了呢?或许,我们应该把重点放在FileMode,FileAccess,FileShare这三个枚举身上,说不定就是它们搞的鬼。

FileMode

MSDN上的解释是指定操作系统打开文件的方式,我想这个应该不需要解释了,大家平时用得比较多了。MSDN的表格也很好的阐述了各个枚举值的作用,我就不在解释了。

FileAccess

定义用于文件读取、写入或读取/写入访问权限的常数。

这个枚举也用得比较多了,描述也很通俗易懂,我也不便再解释了。^_^!

FileShare

相信这个枚举类型大家会比较陌生,甚至有同学见都没见过(惭愧的是,我也是才认识它没多久),陌生归陌生,但它的作用力也是不可低估,只是.Net帮我们把它封装得比较好,以至于我们一度认为它不是什么重要角色。好吧,进入主题!

包含用于控制其他 FileStream 对象对同一文件可以具有的访问类型的常数。这句话是什么意思呢?说实话,我现在看句话还是觉得很纠结,相信很多同学看到也是一头雾水,没关系,我们先跳过!

看它的成员描述,和FileAccess很是相似,那我们就尝试着来揭开它暂时神秘的面纱吧!

FileShare.Read

从字面上的意思,我们可以理解为首先打开一个文件之后(资源未释放),我们可以再用只读的方式读取文件从而不会抛出文件无法访问的异常。利用刚才实现的方法,可以轻易的再完成这个实验:


代码如下:

WriteFile(FileMode.Create, FileAccess.Write, FileShare.Read);
ReadFile(FileAccess.Read, FileShare.Read);

这是什么回事?不是都设置成已读了吗?或许只能在读文件的时候才能设置为只读共享。我们再尝试一下:


代码如下:

ReadFile(FileAccess.Read, FileShare.Read);
ReadFile(FileAccess.Read, FileShare.Read);

这次的确是能在第一次没释放资源时再读,那我们再试试能否在设置只读共享后写文件:


代码如下:

ReadFile(FileAccess.Read, FileShare.Read);
WriteFile(FileMode.Create, FileAccess.Write, FileShare.Read);

首先正确的读出了文件的内容,但当我尝试写入一些内容的时候却又报错了。那么,根据以上的实验,就可以得知这个只读的共享只有是在连续读取文件才有效!

FileShare.Write

结合Read的经验,字面上的意思应该可以理解为,只有在写文件时设置共享方式为Write,随后才能继续写入文件,否则会抛出异常。这里比较好玩的时,设置Write之后,万能的Window记事本也打不开文件了。

FileShare.ReadWrite

有了以上的经验,从字面上理解,可以认为这个ReadWrite一定是结合了Read和Write的特性。那到底它有什么用呢?上面我们知道,在读文件设置Read共享能继续读而不能写,在写文件时设置Write共享则能继续写而不能读,但是当我们设置了写共享后并想读取文件时怎么办?只能先释放资源再重新加载了吗?不需要,ReadWrite就是为此而生的。


代码如下:

WriteFile(FileMode.Create, FileAccess.Write, FileShare.Read);
ReadFile(FileAccess.Read, FileShare.ReadWrite);

不过这里写文件的时候并不允许把共享设置成Write,否则读文件时用ReadWrite则无效(报异常),但都设置为ReadWrite可以。这一定,便可以解决很多日常开发中的烦恼。

FileShare.None/FileShare.Delete

有了上面的经验,相信这两个你也很容易的就理解了,None则为不允许后续有任何操作,而Delete则是允许你随后进行删除操作。

黑箱子里的内容

对于文件操作,我们平常使用的比较多的可能是以下几种:


代码如下:

File.AppendAllText("......");
File.AppendAllLines(...);
File.AppendText(...);
FileStream fs = new FileStream(path, FileAccess.Write);
fs.Write(....);

实际上它们也是在内部初始化了FileMode/FileAccess/FileShare,例如File的静态方法最后都会生成一

个Stream实例,其中便调用了私有方法

尾声

现在,我们明白了,其实/FileShare就是控制文件流的“访问权限”,当然,这仅仅是入门的文件操作,自己做了笔记,也希望能给大家带来帮助!

(0)

相关推荐

  • C# 遍历枚举类型的所有元素

    比如定义了一个错误的枚举类型 复制代码 代码如下: public enum eErrorDetailCode : int         {             登陆成功 = 0,             登出 = 1,             应用错误 = 2,             成功 = 16,             失败 = 17         } 需要引用 using System; 然后在循环中,遍历枚举对象的所有元素 复制代码 代码如下: foreach (int  m

  • 枚举类型的定义和应用总结

    定义:如果一种变量只有几种可能的值,可以定义为枚举类型.所谓"枚举类型"是将变量的值一一列举出来,变量的值只能在列举出来的值的范围内. 声明:声明枚举类型用enum开头,例如: 复制代码 代码如下: enum weekday{sun,mon,tue,wed,thu,fri,sat}; 上面声明了一个枚举类型weekday,花括号中的sun,mon,....sat等称为枚举元素或枚举常量. 这些枚举常量,不会因为我们写了sun就自动代表着"星期天",它只是一个符号,究

  • C#枚举数值与名称的转换实例分享

    首先建立一个枚举: 复制代码 代码如下: /// <summary>    /// 颜色    /// </summary>    public enum ColorType    {        /// <summary>        /// 红色         /// </summary>        Red, /// <summary>        /// 蓝色         /// </summary>      

  • C# 获取枚举值的简单实例

    先申明一个枚举: 复制代码 代码如下: public enum Test_Enum        {            one = 1001, two = 1002, three = 1003, four = 1004, five = 1005, six = 1006, seven = 1007, eight = 1008, nine = 1009, zero = 1000        } 获取值: 复制代码 代码如下: object ojb = Enum.GetName(typeof(T

  • 枚举的用法详细总结

    1.枚举enum的用途浅例写程序时,我们常常需要为某个对象关联一组可选alternative属性.例如,学生的成绩分A,B,C,D等,天气分sunny, cloudy, rainy等等.更常见的,打开一个文件可能有三种状态:input, output和append. 典型做法是,对应定义3个常数,即:const int input = 1;const int output = 2;const int append = 3;然后,调用以下函数:bool open_file(string file_

  • FileShare枚举的使用小结(文件读写锁)

    开发过程中,我们往往需要大量与文件交互,读文件,写文件已成家常便饭,本地运行完美,但一上到投产环境,往往会出现很多令人措手不及的意外,或开发中的烦恼,因此,我对普通的C#文件操作做了一次总结,问题大部分如下: 1:写入一些内容到某个文件中,在另一个进程/线程/后续操作中要读取文件内容的时候报异常,提示 System.IO.IOException: 文件"XXX"正由另一进程使用,因此该进程无法访问此文件. 2:在对一个文件进行一些操作后(读/写),随后想追加依然报System.IO.I

  • asp.net 细说文件读写操作(读写锁)

    问题大部分如下: 1:写入一些内容到某个文件中,在另一个进程/线程/后续操作中要读取文件内容的时候报异常,提示 System.IO.IOException: 文件"XXX"正由另一进程使用,因此该进程无法访问此文件. 2:在对一个文件进行一些操作后(读/写),随后想追加依然报System.IO.IOException: 文件"XXX"正由另一进程使用,因此该进程无法访问此文件.次问题与1相似. 3:对一个文件进行一些操作后,想删除文件,依然报System.IO.IO

  • C#在复杂多线程环境下使用读写锁同步写入文件

    代码一: class Program { static int LogCount = 1000; static int SumLogCount = 0; static int WritedCount = 0; static int FailedCount = 0; static void Main(string[] args) { //往线程池里添加一个任务,迭代写入N个日志 SumLogCount += LogCount; ThreadPool.QueueUserWorkItem((obj)

  • PHP程序中的文件锁、互斥锁、读写锁使用技巧解析

    文件锁 全名叫 advisory file lock, 书中有提及. 这类锁比较常见,例如 mysql, php-fpm 启动之后都会有一个pid文件记录了进程id,这个文件就是文件锁. 这个锁可以防止重复运行一个进程,例如在使用crontab时,限定每一分钟执行一个任务,但这个进程运行时间可能超过一分钟,如果不用进程锁解决冲突的话两个进程一起执行就会有问题. 使用PID文件锁还有一个好处,方便进程向自己发停止或者重启信号.例如重启php-fpm的命令为 kill -USR2 `cat /usr

  • python版本的读写锁操作方法

    本文实例讲述了python版本的读写锁操作方法.分享给大家供大家参考,具体如下: 最近要用到读写锁的机制,但是python2.7的自带库里居然木有. 网上讲读写锁的例子众多,但是原理简单,代码明晰的却不多见, 索性自己写个. 读写锁一般用于多个读者,1个或多个写者同时访问某种资源的时候.多个读者之间是可以共享资源的,但是写者与读者之间,写者与写者之间是资源互斥的. 这也就是说同时可以有多个读者或一个写者处于工作状态. 细分下来,读写锁可以分为三类,读者优先,写者优先和公开策略. 第一种,读者优先

  • GO语言并发编程之互斥锁、读写锁详解

    在本节,我们对Go语言所提供的与锁有关的API进行说明.这包括了互斥锁和读写锁.我们在第6章描述过互斥锁,但却没有提到过读写锁.这两种锁对于传统的并发程序来说都是非常常用和重要的. 一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法--Lock和Unlock.顾名思义,前者被用于锁定当前的互斥量,而后者则被用来对当前的互斥量进行解锁. 类型sy

  • 深入多线程之:Reader与Write Locks(读写锁)的使用详解

    线程安全的一个很经常的需求是允许并发读,但是不允许并发写,例如对于文件就是这样的. ReaderWriterLockSlim 在.net framework 3.5的时候就提供了,它是用来代替以前的"fat"版本的"ReaderWriterLock" 这两个类,有两种基本的锁----一个读锁,一个写锁. 写锁是一个完全排他锁. 读锁可以和其他的读锁兼容 因此当一个线程持有写锁的是很,所有的尝试获取读锁和写锁的线程全部阻塞,但是如果没有一个线程持有写锁,那么可以有一系

  • C#解决SQlite并发异常问题的方法(使用读写锁)

    本文实例讲述了C#解决SQlite并发异常问题的方法.分享给大家供大家参考,具体如下: 使用C#访问sqlite时,常会遇到多线程并发导致SQLITE数据库损坏的问题. SQLite是文件级别的数据库,其锁也是文件级别的:多个线程可以同时读,但是同时只能有一个线程写.Android提供了SqliteOpenHelper类,加入Java的锁机制以便调用.但在C#中未提供类似功能. 作者利用读写锁(ReaderWriterLock),达到了多线程安全访问的目标. using System; usin

  • 举例说明Java多线程编程中读写锁的使用

    以下示例为 java api并发库中 ReentrantReadWriteLock自带的实例,下面进行解读 class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock();//@1 if (!cacheValid) { //

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

    在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件. 选择最后一种方法实现的时候,若对文件操作与线程同步不熟悉,问题就有可能出现了,因为同一个文件并不允许多个线程同时写入,否则会提示"文件正在由另一进程使用,因此该进程无法访问此文件". 这是文件的并发写入问题,就需要用到线程同步.而微软也给线程同步提供了一些相关的类可以达到这样的目的,本文使用到的 System.Thr

随机推荐