请问关于使用Filestream读取同一文件的问题
在多线程中,A线程使用Filestream往文件F中写数据,B线程使用另一Filestream从F中读取数据;A每次写数据后调用flush并更新数据长度L,B则根据数据长度L读数据,但是B读出的数据可能是空数据,貌似A虽然调用了Flush但实际还没有写数据到文件中,这有可能吗?如果想避免这种情况,又需要保证B读取不滞后于A的写入,有什么好办法吗 --------------------编程问答-------------------- 可以看看下面这个例子:
如何使用异步模式读取一个文件
异步模式是在处理流类型时经常采用的一种方式,其应用的领域相当广阔,包括读写文件、网络传输、读写数据库,甚至可以采用异步模式来做任何计算工作。相对于手动编写线程代码,异步模式是一个高效的编程模式。
所涉及的知识点
· 异步模式的概念
· 异步读取文件内容
分析问题
所谓的异步模式,是指在启动一个操作之后可以继续执行其他工作,而不必等待操作的结束。以读取文件为例,在同步模式下,当程序执行到Read方法时,需要等到读取动作结束后才能继续往下执行。而异步模式则可以简单地通知开始读取任务之后,继续其他的操作。异步模式的优点即在于不需要使当前线程等待,而可以充分地利用CPU时间。
在.NET中,很多类型都支持异步模式的编程,其共有的特性是:
· 调用一个形似BeginXXX的方法,来表明开始异步执行某操作。
· 在调用了BeginXXX方法之后,主线程可以继续执行任意的代码,而无须关心异步操作的情况。
· 以异步聚集技巧来查看异步操作的结果。
· 调用EndXXX方法来表示一个异步操作结束。
异步模式区别于线程池机制的地方在于其允许程序查看操作的执行状态,而如果利用线程池的后台线程,则无法确切地知道操作的进行状态以及其是否已经结束。
所谓的聚集技巧,是指查看异步操作是否结束的方法,常用的聚集技巧有三种:
· 直接调用EndXXX方法,如果异步操作还未执行,主线程会被阻止直至异步操作结束。
· 查看调用BeginXXX后得到的IAsyncResult对象的IsCompleted属性。
· 在调用BeginXXX时传入操作结束后需要执行的方法,同时把执行异步操作的对象传入以便执行EndXXX方法。
在三种常见的聚集技巧中,第三种充分利用了异步操作带来的效率,完全避免了主线程的等待,笔者建议读者在使用异步模式时采用第三种聚集技巧。下列代码展示了如何使用异步编程模式来打开一个文件,并且采用刚才介绍的第三种技巧来完成读取完文件的后续工作。
主线程负责开始异步读取并且传入聚集时需要使用的方法和状态对象,如代码7-7所示。
代码7-7 异步读取文件:AsyncReadFile.cs
partial class AsyncReadFile
{
//测试文件
const String _testFile = "C:\\TestAsyncRead.txt";
static void Main(string[] args)
{
try
{
//创建测试文件
if (File.Exists(_testFile))
File.Delete(_testFile);
//写入一些内容,以供读取
using (FileStream fs = File.Create(_testFile))
{
String content = "我是文件内容。";
Byte[] contentbyte = Encoding.Default.GetBytes(content);
fs.Write(contentbyte, 0, contentbyte.Length);
}
//开始异步读取文件内容
using (FileStream fs = new FileStream(_testFile, FileMode.Open,
FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous))
{
Byte[] data = new Byte[1024];
ReadFileClass rfc = new ReadFileClass(fs, data);
//这里开始异步读取
IAsyncResult ir = fs.BeginRead(data, 0, 1024, FinishReading, rfc);
//这里模拟做了一些其他的工作
Thread.Sleep(3 * 1000);
Console.Read();
}
}
finally
{
//这里做清理工作
try
{
if (File.Exists(_testFile))
File.Delete(_testFile);
}
finally { }
}
}
}
代码7-8定义了完成异步读取之后需要调用的方法,其逻辑是简单地打印出文件的内容。
代码7-8 异步读取文件:AsyncReadFile.cs
partial class AsyncReadFile
{
/// <summary>
/// 完成异步读取时调用的方法
/// </summary>
/// <param name="ir">状态对象</param>
static void FinishReading(IAsyncResult ir)
{
ReadFileClass rfc = (ReadFileClass)ir.AsyncState;
//这一步是必须的
//这会让异步读取占用的资源被释放
int length = rfc._fs.EndRead(ir);
Console.WriteLine("读取文件结束。\r\n文件的长度为:{0}\r\n文件内容为:",length.ToString());
Byte[] result = new Byte[length];
Array.Copy(rfc._data, 0, result, 0, length);
Console.WriteLine(Encoding.Default.GetString(result));
}
}
代码7-9定义了作为状态对象传递的类型,这个类型对所有需要传递的数据进行打包。
代码7-9 异步读取文件:AsyncReadFile.cs
/// <summary>
/// 打包传递给完成异步后回调的方法
/// </summary>
class ReadFileClass
{
//以便回调方法中释放异步读取的资源
public FileStream _fs;
//文件内容
public Byte[] _data;
public ReadFileClass(FileStream fs, Byte[] data)
{
_fs = fs;
_data = data;
}
}
运行这段程序,将得到如下的结果:
读取文件结束。
文件的长度为:14
文件内容为:
我是文件内容。
正如程序所展现的,使用回调方法聚集的异步模式需要花费一点额外的代码量,因为它需要把异步操作的对象及操作的结果数据都打包到一个类型里以便能够传递给回调的委托方法,这样在委托方法中才能够有机会处理操作的结果,并且调用EndXXX方法以释放资源。
执行EndXXX操作是必需的,因为该方易做图保证.NET为执行异步操作而分配的一些资源被释放掉。需要读者注意的是,该方法必须由调用BeginXXX方法的引用对象来执行,任何指向同一对象的其他引用都不能代替执行EndXXX方法,否则会导致异常。 --------------------编程问答-------------------- 可以使用线程互斥或同步来处理。 --------------------编程问答-------------------- 使用线程同步来做 --------------------编程问答-------------------- 用Lock,避免多线程同时使用!
lock (this)
{
。。。。
} --------------------编程问答-------------------- 好像不是这个,不过还是谢谢
--------------------编程问答-------------------- 使用lock好像也还不行
问题在于一个stream写完后,另一个stream读取其刚写的内容时出问题
--------------------编程问答-------------------- “问题在于一个stream写完后,另一个stream读取其刚写的内容时出问题”
stream写完后:
FileStream.Flush();
FileStream.Close ();
FileStream.Dispose ()
--------------------编程问答-------------------- 用完要释放,不要让stream保持着 --------------------编程问答-------------------- 因为写和读都是持续的,如果每写一段数据都Filestream.close(),Filestream.dispose()
那写完之后又需要重新再实例化新的Filestream用于写下面的数据
这样性能会比较低吧
--------------------编程问答-------------------- 当然是读、写整个过程做完再用那些代码 --------------------编程问答-------------------- 我的意思是同一文件
Filestream(A).write之后即使调用flush
Filstream(B)进行read也可能读不到A刚写的数据,而是出来一堆空数据
补充:.NET技术 , C#