I'm writing a method which asynchronously writes separate lines of text to a file. If it's cancelled it deletes the created file and jumps out of the loop.
This is the simplified code which works fine... And I marked 2 points which I'm not sure how they are being handled. I want the code to not block the thread in any case.
public async Task<IErrorResult> WriteToFileAsync(string filePath,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var stream = new FileStream(filePath, FileMode.Create);
using var writer = new StreamWriter(stream, Encoding.UTF8);
foreach (var line in Lines)
{
if (cancellationToken.IsCancellationRequested)
{
//
// [1] close, delete and throw if cancelled
//
writer.Close();
stream.Close();
if (File.Exists(filePath))
File.Delete(filePath);
throw new OperationCanceledException();
}
// write to the stream
await writer.WriteLineAsync(line.ToString());
}
//
// [2] flush and let them dispose
//
await writer.FlushAsync();
await stream.FlushAsync();
// await stream.DisposeAsync(); ??????
return null;
}
1
I'm calling Close() on FileStream and StreamWriter and I think it will run synchronously and blocks the thread. How can I improve this? I don't want to wait for it to flush the buffer into the file and then delete the file.
2
I suppose the Dispose method will be called and not DisposeAsync at the end of the using scope. (is this assumption correct?).
So Dispose blocks the thread and in order to prevent that I'm flushing first with FlushAsync so that Dispose would perform less things. (to what extent is this true?)
I could also remove using and instead I could write DisposeAsync manually in these two places. But it will decrease readability.
If I open the FileStream with useAsync = true would it automatically call DisposeAsync when using block ends?
Any explanation or a variation of the above code which performs better is appreciated.
As you have it, the using statement will call Dispose(), not DisposeAsync().
C# 8 brought a new await using syntax, but for some reason it's not mentioned in the What's new in C# 8.0 article.
But it's mentioned elsewhere.
await using var stream = new FileStream(filePath, FileMode.Create);
await using var writer = new StreamWriter(stream, Encoding.UTF8);
But also note that this will only work if:
You're using .NET Core 3.0+ since that's when IAsyncDisposable was introduced, or
Install the Microsoft.Bcl.AsyncInterfaces NuGet package. Although this only adds the interfaces and doesn't include the versions of the Stream types (FileStream, StreamWriter, etc.) that use it.
Even in the Announcing .NET Core 3.0 article, IAsyncDisposable is only mentioned in passing and never expanded on.
On another note, you don't need to do this (I see why now):
writer.Close();
stream.Close();
Since the documentation for Close says:
This method calls Dispose, specifying true to release all resources. You do not have to specifically call the Close method. Instead, ensure that every Stream object is properly disposed.
Since you're using using, Dispose() (or DisposeAsync()) will be called automatically and Close won't do anything that's not already happening.
So if you do need to specifically close the file, but want to do it asynchronously, just call DisposeAsync() instead. It does the same thing.
await writer.DisposeAsync();
public async Task<IErrorResult> WriteToFileAsync(string filePath,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await using var stream = new FileStream(filePath, FileMode.Create);
await using var writer = new StreamWriter(stream, Encoding.UTF8);
foreach (var line in Lines)
{
if (cancellationToken.IsCancellationRequested)
{
// not possible to discard, FlushAsync is covered in DisposeAsync
await writer.DisposeAsync(); // use DisposeAsync instead of Close to not block
if (File.Exists(filePath))
File.Delete(filePath);
throw new OperationCanceledException();
}
// write to the stream
await writer.WriteLineAsync(line.ToString());
}
// FlushAsync is covered in DisposeAsync
return null;
}
Related
Consider the following code taken from the the Microsoft docs:
using FileStream createStream = File.Create(fileName);
// ...write to stream etc..
await createStream.DisposeAsync(); // <- isn't this done automatically because of the using clause in the first line`?
Isn't calling the DisposeAsync() method superfluous?
using clause will call Dispose() method, not DisposeAsync(). It's not the same since Dispose() is blocking call and the presence of DisposeAsync suggests that dispose might be resource intensive and so you don't want to call Dispose if you can instead call DisposeAsync.
The code as written will first call DisposeAsync and then at the end of the scope it will also call Dispose. We can assume it's harmless because implementing class should have a check that resource was already disposed so second Dispose should do nothing. However, if you use C#8+, you can use await using:
await using FileStream createStream = File.Create(fileName);
This is the same as using but it will call await DisposeAsync() at the end of the scope, instead of Dispose. So just like you do now, but automatically (and in finally block). It works with IAsyncDisposable target (such as FileStream which inherits from Stream which implements IAsyncDisposable).
You need to check the whole snippet here:
public static async Task Main()
{
var weatherForecast = new WeatherForecast
{
// ...
};
string fileName = "WeatherForecast.json";
using FileStream createStream = File.Create(fileName);
await JsonSerializer.SerializeAsync(createStream, weatherForecast);
await createStream.DisposeAsync();
Console.WriteLine(File.ReadAllText(fileName));
}
using declaration will call Dispose in generated finally block at the end of the current scope, the problem here is that the file is accessed before the end of the scope:
Console.WriteLine(File.ReadAllText(fileName));
So the await createStream.DisposeAsync(); is actually needed otherwise the file will not be accessible (or even if it would be - potentially not all data will be flushed).
In this concrete case switching to using statement (the one with braces, also both using statement and declaration actually support handling IAsyncDisposable) would be cleaner and will make await createStream.DisposeAsync(); call redundant:
await using (FileStream createStream = File.Create(fileName))
{
await JsonSerializer.SerializeAsync(createStream, weatherForecast);
}
Console.WriteLine(File.ReadAllText(fileName));
I want to write to StreamWriter line async but I don't want to await on this.
for(int i= 0 ;i<1000;i++)
{
sw.WriteLineAsync(i.ToString());
}
But i got an error that i invoke to WriteLineAsync in same time.
What can I do to fix that?
I want to close this StreamWriter after this loop.
How can I verify that I now close without to write all data on StreamWriter, Or when I close the stream all the data that sent with WriteLineAsync will be write before the stream close?
If you want to write the lines using async, you should use await. Because, the file might be corrupted and you might encounter with possible stream errors like "the stream is already in use". In short, you should synchronize the write action.
So, I provided an example;
private async Task WriteToFileAsAsync()
{
string file = #"sample.txt";
using (FileStream stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite))
{
using (StreamWriter streamWriter = new StreamWriter(stream))
{
for (int i = 0; i < 1000; i++)
{
await streamWriter.WriteLineAsync(i.ToString());
}
}
}
}
Also, you can close and dispose the StreamWriter within using blocks.
EDIT
If you want to perform write action in separately from main thread, don't use async methods and just create a seperate Task and assign it an another thread.
private void WriteToFile()
{
string file = #"sample.txt";
using (FileStream stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite))
{
using (StreamWriter streamWriter = new StreamWriter(stream))
{
for (int i = 0; i < 1000; i++)
{
streamWriter.WriteLine(i.ToString());
}
}
}
}
Then call like this;
Task.Factory.StartNew(WriteToFile);
In your particular context there are two main issues in doing what you would like to do.
Technically speaking, if you call an async method, you need to await for it sooner or later, hence you can collect the task, and await for it later on. However, the WriteLineAsync method is not atomic, therefore calling it and performing other operations on the stream can corrupt the stream itself.
If you don't want to await, then don't call the Async method..
If you want to close a stream after using, use using with it.
using (StreamWriter writer = new StreamWriter("temp.txt"))
{
for (int i = 0; i < 1000; i++)
{
writer.WriteLine(i.ToString());
}
}
If you want it to be asynchronous, then you have to call the Async version, otherwise it will corrupt the file.
I wrote below code for sending a photo to my bot, but in my stream, I have two exceptions for read and write and my photo was not send.
I think maybe the reason was this error, but I couldn't fix it:
stream.ReadTimeout threw an exception of type 'System.InvalidOperationException'
using (var stream = System.IO.File.Open("a.jpg", FileMode.Open))
{
var fileToSend = new FileToSend("a.jpg", stream);
Task.Run(() => bot.SendPhotoAsync(u.Message.Chat.Id, fileToSend).ConfigureAwait(false));
}
The reason for this exception is probably that you Dispose the stream immediatly after starting the task.
The using statement calls Dispose on the stream instance when execution leaves this block. You can either remove this using statement or - if your method already is async - you may simply await the call to SendPhotoAsync(). There is no reason to use another thread with Task.Run():
using (var stream = System.IO.File.Open("a.jpg", FileMode.Open))
{
var fileToSend = new FileToSend("a.jpg", stream);
await bot.SendPhotoAsync(u.Message.Chat.Id, fileToSend).ConfigureAwait(false);
}
The state-machine created by the compiler for this await call takes care that the finally block of the using statement (where stream.Dispose() will be called) is executed only after the Task returned by SendPhotoAsync has completed.
In an UWP app, I'm experiencing problems with the usage of DataWriter. When I deploy the UWP app to an ARM machine (RaspBerry PI 2B) it only occasionally succeeds to write a string to the serialdevice. Find my code below. Note: this function only gets called every 30 seconds or so.
public async Task Transmit(string command)
{
if (serialPort == null)
serialPort = await GetSerialDeviceAsync();
dataWriter = new DataWriter(serialPort.OutputStream);
dataWriter.WriteString(command);
await dataWriter.StoreAsync();
await dataWriter.FlushAsync();
dataWriter.DetachStream();
dataWriter.Dispose();
}
I have two questions:
Can someone confirm that I'm using the correct approach of DataWriter?
If I would want to reuse the dataWriter instead of defining a new DataWriter() every time, should I Flush and Detach the stream?
Yes you are using DataWriter correctly. I would recommend that you continue to dispose of the DataWriter each time instead of re using it. You should also be using the using statement instead of Dispose() e.g.:
using (var dataWriter = new DataWriter(serialPort.OutputStream))
{
...
}
This accomplishes the same thing as placing Dispose() within the finally block of a try catch finally.
I have a Task that reads strings from a blocking collection and is supposed to write them out to a file. Trouble is, while the file is created, the size of the file is 0 bytes after the task completes.
While debugging, I see that non-empty lines are retrieved from the blocking collection, and the stream writer is wrapped in a using block.
For debugging I threw in a flush that should not be required and write the lines to the console. There are 100 non-empty lines of text read from the blocking collection.
// Stuff is placed in writeQueue from a different task
BlockingCollection<string> writeQueue = new BlockingCollection<string>();
Task writer = Task.Factory.StartNew(() =>
{
try
{
while (true)
{
using (FileStream fsOut = new FileStream(destinationPath, FileMode.Create, FileAccess.Write))
using (BufferedStream bsOut = new BufferedStream(fsOut))
using (StreamWriter sw = new StreamWriter(bsOut))
{
string line = writeQueue.Take();
Console.WriteLine(line); // Stuff is written to the console
sw.WriteLine(line);
sw.Flush(); // Just in case, makes no difference
}
}
}
catch (InvalidOperationException)
{
// We're done.
}
});
Stepping through in the debugger, I see that the program terminates in an orderly manner. There are no unhandled exceptions.
What might be going wrong here?
You are re-creating the file on every run of the loop. Change the FileMode.Create to FileMode.Append and it will keep the previous values you wrote on it.
Also, using exceptions to detect that you should stop is a really bad practice, if this a consumer-producer solution, you can easily do better by having the producer setting a thread safe flag variable signaling it has finished the work and will not produce anything else.