当前位置:编程学习 > C#/ASP.NET >>

多线程写文件的问题

我用多线程调用下面的代码时,有的时候可以正确完成任务,有的时候在构造FileStream对象时出错,说是另一个进程正在访问。
查查MSDN,关于FileShare.Write部分写的是:“允许随后打开文件写入。如果未指定此标志,则文件关闭前,任何打开该文件以进行写入的请求(由此进程或另一进过程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。 ”
他的最后一句话叫我很难理解,什么是附加权限?
这段程序的问题究竟出在哪里?

            //创建一个FileStream对象和一个BinaryWriter对象,用于将数据写进文件
            FileStream fileStream = null ;
            BinaryWriter binaryWriter = null;

            try
            {
                fileStream = fileInfo.Open(FileMode.Open, FileAccess.Write, FileShare.Write);//注意Open方法的参数
            }
            catch(IOException e)
            {
                throw new IOException("构造FileStream对象时出错",e);
            }

            try
            {
                binaryWriter = new BinaryWriter(fileStream);
            }
            catch(IOException e)
            {
                fileStream.Close();
                throw new IOException("构造BinaryWriter对象时出错",e);
            }

            try
            {
                //将该流的当前位置设置为适当的位置
                fileStream.Seek(request.Offset, SeekOrigin.Begin);//此方法可能出现出现 I/O 错误,抛出IOException异常

                //将数据块写入文件
                binaryWriter.Write(request.DataBlock);//此方法可能出现出现 I/O 错误,抛出IOException异常
            }
            catch (IOException e)
            {
                throw new IOException("将数据块写入文件时出错",e);
            }
            finally
            {
                //关闭流,BinaryWriter也将关闭FileStream对象,所以无需再调用fileStream.Close()
                binaryWriter.Close();
            } --------------------编程问答-------------------- 自己顶一下 --------------------编程问答-------------------- 因为文件已经被另外一个线程打开, 你又去打开了 --------------------编程问答-------------------- 多线程写一个文件?有点悬,不如等待再写.

Stream本身写文件的速度不慢吧? --------------------编程问答-------------------- 多线程写文件要加锁。 --------------------编程问答-------------------- 参考多线程读写文件
以下转贴
using   System;   
  using   System.Threading;   
  using   System.IO;   
    
  namespace   CopyTest   
  {   
    
  //FileBuffer用来存放和取出缓冲区变量   
  public   class   FileBuffer{   
  private   int   m_readsize   =   1024;   
  //定义了m_capacity个字节的缓冲区   
  private   byte[]   m_buffer   =   new   byte[4096];   
  //确认缓冲区内已放字节的个数   
  private   int   bufferCount=0;   
  //确定读写的位置   
  private   int   readLocation=0,writeLocation=0;   
    
  public   FileBuffer()   {   
    
  }   
    
  //从缓冲区中取数据   
  public   byte[]   getBuffer()   {   
  //加上了共享锁   
  lock(this)   {   
  //判断如果缓冲区内无内容,则读取者进入wait状态,并且释放对象锁   
  if(bufferCount==0)   {   
  Console.WriteLine("缓冲区无数据,无法读取");   
  Monitor.Wait(this);   
  }   
    
  byte[]   newBuf   =   new   byte[m_readsize];   
  Buffer.BlockCopy(m_buffer,   readLocation,   newBuf,   0,   m_readsize);   
    
  //已经从缓冲区读取了内容,所以bufferCount要进行自减.   
  bufferCount-=m_readsize;   
  //求余的目的是为了循环使用缓冲区   
  readLocation=(readLocation   +   m_readsize)%m_buffer.Length;   
  //通知对象的第一个等待线程可以从WaitSleepJoin转换到Started状态.   
  Monitor.Pulse(this);   
  //返回给读取者取出的数值   
  return   newBuf;   
  }   
  }   
    
  //将数据放入缓冲区   
  public   void   setBuffer(byte[]   writeValue)   {   
  //锁住共享数据区   
  lock(this)   {   
  //如果缓冲区已满,那么进入waitsleepjoin状态   
  if(bufferCount==m_buffer.Length)   {   
  Console.WriteLine("缓冲区溢出!");   
  Monitor.Wait(this);   
  }   
  //向缓冲区写入数据   
  Buffer.BlockCopy(writeValue,   0,   m_buffer,   writeLocation,   m_readsize);   
  //自加,代表缓冲区现在到底有几个数据   
  bufferCount+=m_readsize;   
  //用%实现缓冲区的循环利用   
  writeLocation=(writeLocation   +   m_readsize)%m_buffer.Length;   
  //唤醒waitSleepJoin状态的进程,到started状态   
  Monitor.Pulse(this);   
  }//使用lock隐式的释放了共享锁   
  }   
  }   
    
      
    
      
  //写入者类,向缓冲区中放入数据   
  public   class   Writer   {     
  //定义了同步变量   
  FileBuffer   shared;   
  FileStream   file;   
  //此处构造函数的作用是在启动类中调用写入者的时候,把启动类中定义的sharedLocation传过来   
  public   Writer(FileBuffer   sharedLocation)   {   
  file   =   new   FileStream("C:\\Test.txt",FileMode.Open);   
  shared=sharedLocation;   
  }   
  //定义写入过程   
  public   void   Write()   {   
  //将数据放入缓冲区   
  Byte[]   datas   =   new   byte[1024];   
    
  for(int   byteread=0;byteread<=file.Length;byteread   +=   datas.Length)   {   
  file.Read(datas,   byteread,   datas.Length);   
  shared.setBuffer(datas);   
  }   
    
  file.Close();   
    
  //得到当前线程的名字   
  string   name=Thread.CurrentThread.Name;   
  //此线程执行完毕   
  Console.WriteLine(name+"done   writeing");   
  }   
  }   
    
  public   class   Reader   {//定义读取者   
  byte[]   value;   
  FileStream   file;   
  //定义同步变量   
  FileBuffer   shared;   
  //定义构造函数,负责传递启动类中的shared   
  public   Reader(FileBuffer   sharedLocation)   {   
  file   =   new   FileStream("C:\\Data.txt",FileMode.Create);   
  shared=sharedLocation;   
  }   
    
  public   void   Read()   {   
  //从缓冲区中循环读取   
  for(int   bytewrite=0;bytewrite<=65535;)   {   
  value=shared.getBuffer();   
  file.Write(value,   bytewrite,   value.Length);   
  bytewrite+=value.Length;   
  }   
    
  file.Close();   
    
  //取得当前线程的名字   
  string   name=Thread.CurrentThread.Name;   
  Console.WriteLine(name+"done   reading");   
  }   
  }   
    
  public   class   ThreadTest   {   //设置为启动类   
  public   static   void   Main()   {   
  FileBuffer   shared=new   FileBuffer();   
  //初始化了写入者和读取者,并且把shared参数传递了过去   
  Writer   Writer1=new   Writer(shared);   
  Reader   Reader1=new   Reader(shared);   
    
  Thread   WriterThread   =   new   Thread(new   ThreadStart   (Writer1.Write));   
  WriterThread.Name="写入者";   
    
  Thread   ReaderThread   =   new   Thread(new   ThreadStart   (Reader1.Read));   
  ReaderThread.Name="读取者";   
  //启动这两个线程   
  WriterThread.Start();   
  ReaderThread.Start();   
  WriterThread.Join();   
  ReaderThread.Join();   
  Console.ReadLine();   
  }   
  }   
  }
--------------------编程问答-------------------- 使用Mutex
每个程序在打开文件,进行写的时候,用mutex包含,例如:
Mutex mtx = new Mutex();

mtx.WaitOne();
//Write file here
mtx.ReleaseMutex(); --------------------编程问答-------------------- 我的理解是设置了FileShare.Write,只是说允许写一个文件而在不关闭这个文件的条件下,另外一个进程可以打开这个文件,但具体某个时间点,只允许有一个进程来写文件...

比如A,B两个进程,A打开文件F,对F进行写,此时B可以在A不关闭F的前提下,打开文件F...

但是某一时刻,只能有一个进程A,或者B来对F进行写操作,即不可能A,B同时在写一个文件...

所以鉴于此,可以通过互斥量Mutex加锁来解决这个问题..

不知道我的理解是否正确..
--------------------编程问答-------------------- 楼主的意思是要设置成share写模式,多线程来写吧? --------------------编程问答-------------------- 学习一下 --------------------编程问答-------------------- 多线程写入文件是被MS允许的。要设置属性。 --------------------编程问答-------------------- 多线程写是允许的,但是不加锁的话写出来的东西乱七八糟的 --------------------编程问答-------------------- mark --------------------编程问答-------------------- 我是楼主。
to:liujia_0421(SnowLover)

异常是发生在fileStream = fileInfo.Open(FileMode.Open, FileAccess.Write, FileShare.Write);这里的。
是打开文件时发生的,还没有写呢,所以你的理解可能不对。
我简单测试了一下,上面的语句大概是几千分之一的几率发生异常。

我现在想知道为什么这个异常会发生,而不是如何解决这个问题(我会)。
请大家踊跃发言,谢谢 --------------------编程问答-------------------- TO:而不是如何解决这个问题(我会)。

那请问楼主是怎么解决的呢?分享一下.. --------------------编程问答-------------------- to:liujia_0421(SnowLover)
可以写一个文件流池进行管理FileStream,每一个文件只有一个FileStream。
但是这样写太麻烦,我用的是一个简单的办法。

既然打开流出错是小概率事件,那么我在catch中再试一次,连续两次都出错的几率太小了(不过根据我的需求,不要求100%成功,偶尔出错是允许的)

经过测试,基本上避免异常的发生
            try
            {
                fileStream = fileInfo.Open(FileMode.Open, FileAccess.Write, FileShare.Write);//注意Open方法的参数
            }
            catch (IOException)
            {
                try
                {
                    Thread.Sleep(100);//睡眠100毫秒
                    fileStream = fileInfo.Open(FileMode.Open, FileAccess.Write, FileShare.Write);//注意Open方法的参数
                }
                catch (IOException e)
                {
                    throw new IOException("构造FileStream对象时出错", e);
                }
            } --------------------编程问答-------------------- 楼主的这个想法可以,但还是没有从根本上解决问题...

虽然连续两次出错的几率很小(有多小现在还没法计算出来),是不是已经达到可以忽略的条件,这个还不好说...当然作为权宜之计,这个想法还是不错的... --------------------编程问答-------------------- 如果用append打开,每次追加一行就没有问题 --------------------编程问答-------------------- private static  void WriteFile(object parm)
        {
            FileStream fileStream = null;
            BinaryWriter binaryWriter = null;
            FileInfo fileInfo = new FileInfo("C:\\test.txt");
            try
            {
                fileStream = fileInfo.Open(FileMode.Open, FileAccess.Write, FileShare.Write);//注意Open方法的参数
            }
            catch (IOException e)
            {
                throw new IOException("构造FileStream对象时出错", e);
            }

            try
            {
                binaryWriter = new BinaryWriter(fileStream);
            }
            catch (IOException e)
            {
                fileStream.Close();
                throw new IOException("构造BinaryWriter对象时出错", e);
            }

            try
            {
                //将该流的当前位置设置为适当的位置
                fileStream.Seek(0, SeekOrigin.End);//此方法可能出现出现 I/O 错误,抛出IOException异常

                //将数据块写入文件
                binaryWriter.Write(parm.ToString());//此方法可能出现出现 I/O 错误,抛出IOException异常
            }
            catch (IOException e)
            {
                throw new IOException("将数据块写入文件时出错", e);
            }
            finally
            {
                //关闭流,BinaryWriter也将关闭FileStream对象,所以无需再调用fileStream.Close()
                binaryWriter.Close();
            }
        }


public static void Main()
{
    //多线程写文件
    for (int i = 0; i < 100; i++)
        {
                string str= "This is string " + i + " ";
                System.Threading.Thread thread = new System.Threading.Thread(
                    new System.Threading.ParameterizedThreadStart(WriteFile));
                thread.Start(str);
        }
}


我对你的程序稍稍修改了一下,发现没什么问题啊. 
估计你的情况是由于程序别的部分所造成的,或者换部电脑执行试试. --------------------编程问答-------------------- 学习1下``` --------------------编程问答-------------------- w --------------------编程问答-------------------- 还是要加锁后才能解决冲突问题 --------------------编程问答-------------------- 可以尝试一下加锁和委托 --------------------编程问答-------------------- 弱弱的问一下:一个碟片同一面磁头就是一个,若文件为连续存储,有必要对磁盘文件的写入开多线程吗?
还是我没理解题意? --------------------编程问答-------------------- >弱弱的问一下:一个碟片同一面磁头就是一个,若文件为连续存储,有必要对磁盘文件的写入开>多线程吗?
---------------

比如多线程下载工具 --------------------编程问答-------------------- >弱弱的问一下:一个碟片同一面磁头就是一个,若文件为连续存储,有必要对磁盘文件的写入开>多线程吗?
---------------

比如多线程下载工具
-----------------
原来如此,是我想当然了。 --------------------编程问答-------------------- 可能是你另一个线程正在执行写的时候你再去打开就会包异常.这是问题. --------------------编程问答-------------------- 多线程写是允许的,但是不加锁的话写出来的东西乱七八糟的 --------------------编程问答-------------------- .net竟然没有内存映射文件 的包装机制,晕,玩具 --------------------编程问答-------------------- 飘过...无语...好久没来CSDN了,哈哈... --------------------编程问答-------------------- 为什么不做一个收集器之类的东西,由收集器来负责写文件,其他的线程通过收集器来同步。 --------------------编程问答-------------------- 多线程,易做图烦。 --------------------编程问答-------------------- 给你说个我曾经用过的方法,多线程访问一个文件
建议多设置一个线程专门写文件 而其他需要操作文件的线程直接访问写文件线程
形成队列,关于队列,你可以去微软官方看资料,很多 --------------------编程问答--------------------  莫非这是个典型的多线程同步问题:解决方法可以有 lock mutex 和信号量等线程同步方法
补充:.NET技术 ,  C#
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,