I have the following code,
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
var s = File.Re
Use Stream.ReadAsync for asynchronous reading of file,
private async void Button_Click(object sender, RoutedEventArgs e)
{
string filename = @"c:\Temp\userinputlog.txt";
byte[] result;
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
}
UserInput.Text = System.Text.Encoding.ASCII.GetString(result);
}
Read MSDN Stream.ReadAsync
If you want to read all the lines from file asynchronously then you can use the async
feature to access the file with using FileStream
.
private static async Task<string[]> ReadAllLinesAsync(string filePath)
{
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true))
{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString().Split(new[] { Environment.NewLine },StringSplitOptions.None);
}
}
You can use the async
method inside the event handler by specifying async
to your event handler function.
Here is how you can use this and this would not let your GUI thread to freeze.
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
var s = await ReadAllLinesAsync("Words.txt").ToList();
// do something with s
button1.IsEnabled = true;
}
For more details please see MS Docs
Try this:
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
try
{
var s = await Task.Run(() => File.ReadAllLines("Words.txt").ToList());
// do something with s
}
finally
{
button1.IsEnabled = true;
}
}
Edit:
You don't need the try-finally for this to work. It's really only the one line that you need to change. To explain how it works: This spawns another thread (actually gets one from the thread pool) and gets that thread to read the file. When the file is finished reading then the remainder of the button1_Click method is called (from the GUI thread) with the result. Note that this is probably not the most efficient solution, but it is probably the simplest change to your code which doesn't block the the GUI.
UPDATE: Async versions of File.ReadAll[Lines|Bytes|Text], File.AppendAll[Lines|Text]
and File.WriteAll[Lines|Bytes|Text]
have now been merged into .NET Core and shipped with .NET Core 2.0. They are also included in .NET Standard 2.1.
Using Task.Run
, which essentially is a wrapper for Task.Factory.StartNew
, for asynchronous wrappers is a code smell.
If you don't want to waste a CPU thread by using a blocking function, you should await a truly asynchronous IO method, StreamReader.ReadToEndAsync, like this:
using (var reader = File.OpenText("Words.txt"))
{
var fileText = await reader.ReadToEndAsync();
// Do something with fileText...
}
This will get the whole file as a string
instead of a List<string>
. If you need lines instead, you could easily split the string afterwards, like this:
using (var reader = File.OpenText("Words.txt"))
{
var fileText = await reader.ReadToEndAsync();
return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}
EDIT: Here are some methods to achieve the same code as File.ReadAllLines
, but in a truly asynchronous manner. The code is based on the implementation of File.ReadAllLines itself:
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
public static class FileEx
{
/// <summary>
/// This is the same default buffer size as
/// <see cref="StreamReader"/> and <see cref="FileStream"/>.
/// </summary>
private const int DefaultBufferSize = 4096;
/// <summary>
/// Indicates that
/// 1. The file is to be used for asynchronous reading.
/// 2. The file is to be accessed sequentially from beginning to end.
/// </summary>
private const FileOptions DefaultOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;
public static Task<string[]> ReadAllLinesAsync(string path)
{
return ReadAllLinesAsync(path, Encoding.UTF8);
}
public static async Task<string[]> ReadAllLinesAsync(string path, Encoding encoding)
{
var lines = new List<string>();
// Open the FileStream with the same FileMode, FileAccess
// and FileShare as a call to File.OpenText would've done.
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, DefaultOptions))
using (var reader = new StreamReader(stream, encoding))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
lines.Add(line);
}
}
return lines.ToArray();
}
}
I also encountered a problem described in your question. I've solved it just simplier that in previous answers:
string[] values;
StorageFolder folder = ApplicationData.Current.LocalFolder; // Put your location here.
IList<string> lines = await FileIO.ReadLinesAsync(await folder.GetFileAsync("Words.txt"););
lines.CopyTo(values, 0);