C# Write / Read file, should I use a lock - c#

I need advice whether or not using a lock (ReaderWriterLockSlim).
A user interacts on screen, and data can be saved into a file :
XmlSerializer xmlserializer = new XmlSerializer(typeof(MyFile));
FileStream fs = new FileStream(fileName, FileMode.Create,FileAccess.ReadWrite);
xmlserializer.Serialize(fs, this);
fs.Close();
In parallel, I have a timer (thus same thread, System.Windows.Forms.Timer), which checks this same file size and sends it to a server if modified.
I'll use File.ReadAllBytes as this is a rather small file.
Should I use a lock since writing filestream takes some time ?
I wonder if the timer can cause problem (I don't have a clear understanding if it preempts).
Thanks for any advice.

In WinForms an event never interrupts a running method running in the same thread (i.e. in the UI thread). Any timer_Tick (from System.Windows.Forms.Timer) will be delayed until the serializing code is finished.
(I assume that you are not using async calls.)
You can read the file size directly from the FileStream before closing it.
var xmlserializer = new XmlSerializer(typeof(MyFile));
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite)) {
xmlserializer.Serialize(fs, this);
Console.WriteLine(fs.Length); // <=========
} // The using-statement automatically closes fs
If you need to know whether the file changed in another routine, why don't you just use a flag?
public static bool FileHasChanged { get; set; }
...
var xmlserializer = new XmlSerializer(typeof(MyFile));
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite)) {
xmlserializer.Serialize(fs, this);
}
FileHasChanged = true;
In the other routine (timer_Tick I think):
if (MyFile.FileHasChanged) {
//TODO: Send file to server.
MyFile.FileHasChanged = false;
}
Since everything is running in the same thread, no locking is required.
Another question is whether you really need a file or whether you could just write to a MemoryStream and then use this memory stream to send the data to the server. If you still need the file, you could write to it using the same memory stream and serialize only once. The memory stream would replace the Boolean flag for the communication between the two routines. After sent to the server, the memory stream would be set to null after calling Dispose() (instead of MyFile.FileHasChanged = false;).
That would be more in the sense of Eric Lippert's comments.

Related

can i make Multiple FileStream objects to one file in the same time?

why in fs2 object throw error ?? i already have written a FileShare.ReadWrite in fs object
FileStream fs = new FileStream("hello.txt",FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.ReadWrite);
mama();
Console.ReadKey();
}
static void mama()
{
FileStream fs2 = new FileStream("hello.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
fs2.Read(new byte[3], 0, 3);
}
can any one tell me why this error ?
error = The process cannot access the file 'C:\Users\iP\documents\visual studio 2015\Projects\ConsoleApplication32\ConsoleApplication32\bin\Debug\hello.txt' because it is being used by another process.
You're getting that error because you're passing FileShare.None to the second call. If you change that to FileShare.ReadWrite to match the first call, you won't have that problem.
The reason for this is because the FileStream constructor calls CreateFileW underneath, and if you take a look at the documentation for that function, it states:
You cannot request a sharing mode that conflicts with the access mode
that is specified in an existing request that has an open handle.
CreateFile would fail and the GetLastError function would return
ERROR_SHARING_VIOLATION.
You already have an open handle from the first request using FileAccess.ReadWrite as the access mode, which conflicts with FileShare.None in the second call.
Because your code never closes the file and has an open handle to it
If you can, always use the using statement, it will flush and close the file
using(var fs = new FileStream(...))
{
// do stuff here
} // this is where the file gets flushed and closed
If 2 methods are working on the same file, pass the FileStream in
static void mama(FileStream fs )
{
fs .Read(new byte[3], 0, 3);
}

Await for a process to finish execution

I have a process that takes some varying time to execute. The proceeding part of the code depends on its results.
The process creates a printable file (PRN) file. The proceeding section then reads that file and returns its bytes contents.
When i put a breakpoint at the using statement, i get to read the bytes of the created file and return them to where they are being requested. But when i execute as usual, i get the error.
_ The process cannot access the file 'linkToFile' because it is being used by another process _
lbl.PrintSettings.PrinterName = printerName;
byte[] fileBytes = null;
Task.Run(() => { lbl.Print(int.Parse(qty)); }).Wait(2000);
using (var strm = File.Open(outPutPrintFile,
FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var ms = new MemoryStream())
{
strm.CopyTo(ms);
fileBytes = ms.ToArray();
}
}
return Ok(fileBytes);
I tried to put the part that executes longer in a Task-Wait part but still getting the same error.
Try using Fileshare.ReadWrite instead of FileShare.Read. It's not for some unknown reason as you commented but ReadWrite make sure that further Read/Write operations can be done on opening the file. From your posted code it looks to be the option to choose.

Can I get a GZipStream for a file without writing to intermediate temporary storage?

Can I get a GZipStream for a file on disk without writing the entire compressed content to temporary storage? I'm currently using a temporary file on disk in order to avoid possible memory exhaustion using MemoryStream on very large files (this is working fine).
public void UploadFile(string filename)
{
using (var temporaryFileStream = File.Open("tempfile.tmp", FileMode.CreateNew, FileAccess.ReadWrite))
{
using (var fileStream = File.OpenRead(filename))
using (var compressedStream = new GZipStream(temporaryFileStream, CompressionMode.Compress, true))
{
fileStream.CopyTo(compressedStream);
}
temporaryFileStream.Position = 0;
Uploader.Upload(temporaryFileStream);
}
}
What I'd like to do is eliminate the temporary storage by creating GZipStream, and have it read from the original file only as the Uploader class requests bytes from it. Is such a thing possible? How might such an implementation be structured?
Note that Upload is a static method with signature static void Upload(Stream stream).
Edit: The full code is here if it's useful. I hope I've included all the relevant context in my sample above however.
Yes, this is possible, but not easily with any of the standard .NET stream classes. When I needed to do something like this, I created a new type of stream.
It's basically a circular buffer that allows one producer (writer) and one consumer (reader). It's pretty easy to use. Let me whip up an example. In the meantime, you can adapt the example in the article.
Later: Here's an example that should come close to what you're asking for.
using (var pcStream = new ProducerConsumerStream(BufferSize))
{
// start upload in a thread
var uploadThread = new Thread(UploadThreadProc(pcStream));
uploadThread.Start();
// Open the input file and attach the gzip stream to the pcStream
using (var inputFile = File.OpenRead("inputFilename"))
{
// create gzip stream
using (var gz = new GZipStream(pcStream, CompressionMode.Compress, true))
{
var bytesRead = 0;
var buff = new byte[65536]; // 64K buffer
while ((bytesRead = inputFile.Read(buff, 0, buff.Length)) != 0)
{
gz.Write(buff, 0, bytesRead);
}
}
}
// The entire file has been compressed and copied to the buffer.
// Mark the stream as "input complete".
pcStream.CompleteAdding();
// wait for the upload thread to complete.
uploadThread.Join();
// It's very important that you don't close the pcStream before
// the uploader is done!
}
The upload thread should be pretty simple:
void UploadThreadProc(object state)
{
var pcStream = (ProducerConsumerStream)state;
Uploader.Upload(pcStream);
}
You could, of course, put the producer on a background thread and have the upload be done on the main thread. Or have them both on background threads. I'm not familiar with the semantics of your uploader, so I'll leave that decision to you.

File.AppendAllText vs StreamWriter

I want to create a log file to track some operations in my Application.In my scenario within one session I wanna to log at least 50 time per min.currently im using StremWriter to create log file.
public static StreamWriter InitializeStream(string path)
{
/*ensuring whether thread safe or not*/
lock (mylock)
{
if (null == _stream)
{
var fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite);
_stream = new StreamWriter(fileStream);
return _stream;
}
return _stream;
}
}
//logging operation
StreamHandler.Log(path)
Still im bit confuse to selecting AppendAllText or StreamWriter.
Instead of StreamWriter way can i get advantage using AppendAllText Directly?(Performance)
Do not use AppendAllText() inside the loop because it uses StremWriter internally where stream object will be initialized and disposed on each iteration.

Close a filestream without Flush()

Can I close a file stream without calling Flush (in C#)? I understood that Close and Dispose calls the Flush method first.
MSDN is not 100% clear, but Jon Skeet is saying "Flush", so do it before close/dispose. It won't hurt, right?
From FileStream.Close Method:
Any data previously written to the buffer is copied to the file before
the file stream is closed, so it is not necessary to call Flush before
invoking Close. Following a call to Close, any operations on the file
stream might raise exceptions. After Close has been called once, it
does nothing if called again.
Dispose is not as clear:
This method disposes the stream, by writing any changes to the backing
store and closing the stream to release resources.
Remark: the commentators might be right, it's not 100% clear from the Flush:
Override Flush on streams that implement a buffer. Use this method to
move any information from an underlying buffer to its destination,
clear the buffer, or both. Depending upon the state of the object, you
might have to modify the current position within the stream (for
example, if the underlying stream supports seeking). For additional
information see CanSeek.
When using the StreamWriter or BinaryWriter class, do not flush the
base Stream object. Instead, use the class's Flush or Close method,
which makes sure that the data is flushed to the underlying stream
first and then written to the file.
TESTS:
var textBytes = Encoding.ASCII.GetBytes("Test123");
using (var fileTest = System.IO.File.Open(#"c:\temp\fileNoCloseNoFlush.txt", FileMode.CreateNew))
{
fileTest.Write(textBytes,0,textBytes.Length);
}
using (var fileTest = System.IO.File.Open(#"c:\temp\fileCloseNoFlush.txt", FileMode.CreateNew))
{
fileTest.Write(textBytes, 0, textBytes.Length);
fileTest.Close();
}
using (var fileTest = System.IO.File.Open(#"c:\temp\fileFlushNoClose.txt", FileMode.CreateNew))
{
fileTest.Write(textBytes, 0, textBytes.Length);
fileTest.Flush();
}
using (var fileTest = System.IO.File.Open(#"c:\temp\fileCloseAndFlush.txt", FileMode.CreateNew))
{
fileTest.Write(textBytes, 0, textBytes.Length);
fileTest.Flush();
fileTest.Close();
}
What can I say ... all files got the text - maybe this is just too little data?
Test2
var rnd = new Random();
var size = 1024*1024*10;
var randomBytes = new byte[size];
rnd.NextBytes(randomBytes);
using (var fileTest = System.IO.File.Open(#"c:\temp\fileNoCloseNoFlush.bin", FileMode.CreateNew))
{
fileTest.Write(randomBytes, 0, randomBytes.Length);
}
using (var fileTest = System.IO.File.Open(#"c:\temp\fileCloseNoFlush.bin", FileMode.CreateNew))
{
fileTest.Write(randomBytes, 0, randomBytes.Length);
fileTest.Close();
}
using (var fileTest = System.IO.File.Open(#"c:\temp\fileFlushNoClose.bin", FileMode.CreateNew))
{
fileTest.Write(randomBytes, 0, randomBytes.Length);
fileTest.Flush();
}
using (var fileTest = System.IO.File.Open(#"c:\temp\fileCloseAndFlush.bin", FileMode.CreateNew))
{
fileTest.Write(randomBytes, 0, randomBytes.Length);
fileTest.Flush();
fileTest.Close();
}
And again - every file got its bytes ... to me it looks like it's doing what I read from MSDN: it doesn't matter if you call Flush or Close before dispose ... any thoughts on that?
You don't have to call Flush() on Close()/Dispose(), FileStream will do it for you as you can see from its source code:
http://referencesource.microsoft.com/#mscorlib/system/io/filestream.cs,e23a38af5d11ddd3
[System.Security.SecuritySafeCritical] // auto-generated
protected override void Dispose(bool disposing)
{
// Nothing will be done differently based on whether we are
// disposing vs. finalizing. This is taking advantage of the
// weak ordering between normal finalizable objects & critical
// finalizable objects, which I included in the SafeHandle
// design for FileStream, which would often "just work" when
// finalized.
try {
if (_handle != null && !_handle.IsClosed) {
// Flush data to disk iff we were writing. After
// thinking about this, we also don't need to flush
// our read position, regardless of whether the handle
// was exposed to the user. They probably would NOT
// want us to do this.
if (_writePos > 0) {
FlushWrite(!disposing); // <- Note this
}
}
}
finally {
if (_handle != null && !_handle.IsClosed)
_handle.Dispose();
_canRead = false;
_canWrite = false;
_canSeek = false;
// Don't set the buffer to null, to avoid a NullReferenceException
// when users have a race condition in their code (ie, they call
// Close when calling another method on Stream like Read).
//_buffer = null;
base.Dispose(disposing);
}
}
I've been tracking a newly introduced bug that seems to indicate .NET 4 does not reliably flush changes to disk when the stream is disposed (unlike .NET 2.0 and 3.5, which always did so reliably).
The .NET 4 FileStream class has been heavily modified in .NET 4, and while the Flush*() methods have been rewritten, similar attention seems to have been forgotten for .Dispose().
This is resulting in incomplete files.
Since you've stated that you understood that close & dispose called the flush method if it was not called explicitly by user code, I believe that (by close without flush) you actually want to have a possibility to discard changes made to a FileStream, if necessary.
If that is correct, using a FileStream alone won't help. You will need to load this file into a MemoryStream (or an array, depending on how you modify its contents), and then decide whether you want to save changes or not after you're done.
A problem with this is file size, obviously. FileStream uses limited size write buffers to speed up operations, but once they are depleted, changes need to be flushed. Due to .NET memory limits, you can only expect to load smaller files in memory, if you need to hold them entirely.
An easier alternative would be to make a disk copy of your file, and work on it using a plain FileStream. When finished, if you need to discard changes, simply delete the temporary file, otherwise replace the original with a modified copy.
Wrap the FileStream in a BufferedStream and close the filestream before the buffered stream.
var fs = new FileStream(...);
var bs = new BufferedStream(fs, buffersize);
bs.Write(datatosend, 0, length);
fs.Close();
try {
bs.Close();
}
catch (IOException) {
}
Using Flush() is worthy inside big Loops.
when you have to read and write a big File inside one Loop. In other case the buffer or the computer is big enough, and doesn´t matter to close() without making one Flush() before.
Example: YOU HAVE TO READ A BIG FILE (in one format) AND WRITE IT IN .txt
StreamWriter sw = .... // using StreamWriter
// you read the File ...
// and now you want to write each line for this big File using WriteLine ();
for ( .....) // this is a big Loop because the File is big and has many Lines
{
sw.WriteLine ( *whatever i read* ); //we write here somrewhere ex. one .txt anywhere
sw.Flush(); // each time the sw.flush() is called, the sw.WriteLine is executed
}
sw.Close();
Here it is very important to use Flush(); beacause otherwise each writeLine is save in the buffer and does not write it until the buffer is frull or until the program reaches sw.close();
I hope this helps a little to understand the function of Flush
I think it is safe to use simple using statement, which closes the stream after the call to GetBytes();
public static byte[] GetBytes(string fileName)
{
byte[] buffer = new byte[4096];
using (FileStream fs = new FileStream(fileName))
using (MemoryStream ms = new MemoryStream())
{
fs.BlockCopy(ms, buffer, 4096); // extension method for the Stream class
fs.Close();
return ms.ToByteArray();
}
}

Categories

Resources