多线程写文件的问题
我用多线程调用下面的代码时,有的时候可以正确完成任务,有的时候在构造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#