Why does this async method lock up the user interface? - c#

I am using the following code to read all the images on a network drive and poplate an ImageControl with each, and then display them on the screen.
The problem I'm having is that regardless of making PopulateImages() an async method, and and running Task.WaitAll the user interface is still locked up until all the images render.
Am I doing the async/await incorrectly? What do I need to do to resolve this?
public MainWindow()
{
InitializeComponent();
Loaded += (s, e) => PopulateImages();
}
private async void PopulateImages()
{
string StartDirectory = #"//path/to/network/folder";
Task.WaitAll(Directory
.EnumerateFiles(StartDirectory)
.Select(filename => Task.Run(async () =>
{
Bitmap resizedImage;
using (var sourceStream = File.Open(filename, FileMode.Open))
{
using (var destinationStream = new MemoryStream())
{
await sourceStream.CopyToAsync(destinationStream);
resizedImage = ResizeImage(new Bitmap(destinationStream), 96, 96);
}
}
Dispatcher.BeginInvoke(new Action(() =>
{
var imgControl = new ImageControl(filename, resizedImage);
stackpanelContainer.Children.Add(imgControl);
}));
})).ToArray());
}

You're using Task.WaitAll - that blocks until all the tasks have completed.
Instead, you should use Task.WhenAll, which returns a Task which will itself complete when all the other tasks have completed. You can then await that.
await Task.WhenAll(...);
Although to be honest, unless you need to do anything when the tasks have all completed, you don't need to wait for them at all.

Instead of registering to the Loaded event, consider overriding FrameworkElement.OnInitialized method. That way, you can await on PopulateImage, save the Task.WaitAll and possibly remove the need to use Task.Run, if your ResizeImage isn't too CPU heavy:
public override async void OnInitialized()
{
await PopulateImages();
base.OnInitialized();
}
private async Task PopulateImages()
{
string StartDirectory = #"//path/to/network/folder";
Directory.EnumerateFiles(StartDirectory)
.Select(filename => async () =>
{
Bitmap resizedImage;
using (var sourceStream = File.Open(filename, FileMode.Open))
using (var destinationStream = new MemoryStream())
{
await sourceStream.CopyToAsync(destinationStream);
resizedImage = ResizeImage(new Bitmap(destinationStream), 96, 96);
}
}
var imgControl = new ImageControl(filename, resizedImage);
stackpanelContainer.Children.Add(imgControl);
}

Related

Stream Extensions to convert Stream content into String or Byte array

Using C# 10 I am creating Stream extensions to get content into a String or Byte array.
Something similar to File.ReadAllTextAsync in Microsoft's Net 6.
public static async Task<string> ReadAllTextAsync(this Stream stream). {
string result;
using (var reader = new StreamReader(stream)) {
result = await reader.ReadToEndAsync().ConfigureAwait(false);
}
return result;
}
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream) {
using (var content = new MemoryStream()) {
var buffer = new byte[4096];
int read = await stream.ReadAsync(buffer, 0, 4096).ConfigureAwait(false);
while (read > 0) {
content.Write(buffer, 0, read);
read = await stream.ReadAsync(buffer, 0, 4096).ConfigureAwait(false);
}
return content.ToArray();
}
}
public static async Task<List<string>> ReadAllLinesAsync(this Stream stream) {
var lines = new List<string>();
using (var reader = new StreamReader(stream)) {
string line;
while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) {
lines.Add(line);
}
}
return lines;
}
Is there a better way to do this?
I am not sure about the ConfigureAwait(false) that I picked on some code online.
A better alternative for the ReadAllBytesAsync is
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream)
{
switch (stream)
{
case MemoryStream mem:
return mem.ToArray();
default:
using var m = new MemoryStream();
await stream.CopyToAsync(m);
return mem.ToArray();
}
}
For the ReadAllLinesAsync, the async stream in C# 8 can make the code cleaner:
public IAsyncEnumerable<string> ReadAllLinesAsync(this Stream stream)
{
using var reader = new StreamReader(stream)
while (await reader.ReadLineAsync() is { } line)
{
yield return line;
}
}
notice that the empty brace { } here is actually a property pattern that is only available after C# 8, it checks whether reader.ReadLineAsync() is null, if it's not, assign it to the line variable.
Usage:
var lines = await stream.ReadAllLinesAsync();
await foreach (var line in lines)
{
// write your own logic here
}
P.S.:
The ConfigureAwait(false) is kinda useless if your app is single-threaded like console apps, it instructs the awaiter not to capture the SynchronizationContext and let continuation run on the thread that runs the await statement, this method is useful when you're writing a library or SDK, since your user may use your library in a GUI application, and the combination of block waiting such as calling Task.Wait() and the capturing of SynchronizationContext often leads to deadlock, and ConfigureAwait(false) solves this. For detail explanation see ConfigureAwait FAQ

The calling thread cannot access this object even after adding Dispatcher.Invoke

The calling thread cannot access this object because a different thread owns it even after adding Dispatcher.Invoke.
The problem is still intact even after adding Dispatcher.Invoke.
async Task capturePredict()
{
await Dispatcher.Invoke( async () =>
{
PngBitmapEncoder image = new PngBitmapEncoder();
image.Frames.Add(BitmapFrame.Create(bitmap));
using (Stream stream = File.Create(#"E:\ImageClassificationTraining\image.png"))
{
await Task.Run(() => image.Save(stream));
}
});
}
In contrast to decoding a BitmapSource (which can be frozen to make it cross-thread accessible), encoding can seemingly not be done in a thread other than the UI thread.
You may however separate the encoding step from writing the file, by something like this:
public async Task SaveImageAsync(BitmapSource bitmap, string path)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
memoryStream.Position = 0;
using (var fileStream = File.Create(path))
{
await memoryStream.CopyToAsync(fileStream);
}
}
}

Return stream immediately and then write to stream asynchronously

In my current code I have a method like this to read data from a device (pseudo code):
public async Task<string> ReadAllDataFromDevice()
{
var buffer = "";
using (var device = new Device())
{
while(device.HasMoreData)
{
buffer += await device.ReadLineAsync();
}
}
return buffer;
}
I then want to send all that data via the network to some receiver. The amount of data can be really large. So clearly the above design is not very memory-efficient since it requires to read all the data before I can start sending it to the network socket.
So what I'd like to have is a function that returns a stream instead. Something like this:
public async Task<Stream> ReadAllDataFromDevice()
{
var stream = new MemoryStream();
using (var device = new Device())
using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
{
while(device.HasMoreData)
{
var line = await device.ReadLineAsync();
await streamWriter.WriteLineAsync(line);
}
await streamWriter.FlushAsync();
}
return stream;
}
This returns a stream but it clearly does not solve my problem, because the stream is returned only after all the data has been read from the device.
So I came up with this:
public Stream ReadAllDataFromDevice()
{
var stream = new MemoryStream();
Task.Run(async () => {
using (var device = new Device())
using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
{
while(device.HasMoreData)
{
var line = await device.ReadLineAsync();
await streamWriter.WriteLineAsync(line);
}
await streamWriter.FlushAsync();
}
});
return stream;
}
Is this a good design? I'm especially concerned about thread-safety, lifetime of the stream object used in the lambda, and exception handling.
Or is there a better pattern for this kind of problem?
Edit
Actually I just came up with another design that looks much cleaner to me. Instead of having the ReadAllDataFromDevice() function returning a stream, I let the consumer of the data provide the stream, like this:
public async Task ReadAllDataFromDevice(Stream stream)
{
using (var device = new Device())
using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
{
while(device.HasMoreData)
{
var line = await device.ReadLineAsync();
await streamWriter.WriteLineAsync(line);
}
await streamWriter.FlushAsync();
}
}
This is the design I'm using now:
public async Task ReadAllDataFromDevice(Func<Stream, Task> readCallback)
{
using (var device = new Device())
{
await device.Initialize();
using (var stream = new DeviceStream(device))
{
await readCallback(stream);
}
}
}
The line-by-line device access is encapsulated in the custom DeviceStream class (not shown here).
The consumer of the data would look something like this:
await ReadAllDataFromDevice(async stream => {
using (var streamReader(stream))
{
var data = await streamReader.ReadToEndAsync();
// do something with data
}
});

WP8.1 download file in isolated storage

I am trying to download and save a file in the isolated storage.
This is my attempt of downloading the file
Task.Run(async () => { await DownloadFileFromWeb(new Uri(#"http://main.get4mobile.net/ringtone/ringtone/ibMjbqEYMHUnso8MErZ_UQ/1452584693/fa1b23bb5e35c8aed96b1a5aba43df3d/stefano_gambarelli_feat_pochill-land_on_mars_v2.mp3"), "mymp3.mp3"); }).Wait();
public static Task<Stream> DownloadFile(Uri url)
{
var tcs = new TaskCompletionSource<Stream>();
var wc = new WebClient();
wc.OpenReadCompleted += (s, e) =>
{
if (e.Error != null) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(e.Result);
};
wc.OpenReadAsync(url);
return tcs.Task;
}
public static async Task<Problem> DownloadFileFromWeb(Uri uriToDownload, string fileName)
{
using (Stream mystr = await DownloadFile(uriToDownload))
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream file = new IsolatedStorageFileStream(fileName, FileMode.OpenOrCreate, isf))
{
using (var fs = new StreamWriter(file))
{
byte[] bytesInStream = new byte[mystr.Length];
mystr.Read(bytesInStream, 0, (int)bytesInStream.Length);
file.Write(bytesInStream, 0, bytesInStream.Length);
file.Flush();
}
}
}
return Problem.Ok;
}
Obviously I am doing something wrong here since the file is never and the app is stack forever after the call.
However I believe I am not far from getting there.
Any help is greatly appreciated.
Add this methid and call it from there, it should work.
Downlaod_Click()
public static async void Downlaod_Click()
{
var cts = new CancellationTokenSource();
Problem fileDownloaded = await MainHelper.DownloadFileFromWeb(new Uri(#"url", UriKind.Absolute), "myfile.mp3", cts.Token);
switch (fileDownloaded)
{
case Problem.Ok:
MessageBox.Show("File downloaded");
break;
case Problem.Cancelled:
MessageBox.Show("Download cancelled");
break;
case Problem.Other:
default:
MessageBox.Show("Other problem with download");
break;
}
}
IsolatedStorage is not available in windows 8.1. So you might use following code for Windows 8.1 app, which works fine:
Task.Run(async () => { await DownloadFileFromWeb(new Uri(#"http://main.get4mobile.net/ringtone/ringtone/ibMjbqEYMHUnso8MErZ_UQ/1452584693/fa1b23bb5e35c8aed96b1a5aba43df3d/stefano_gambarelli_feat_pochill-land_on_mars_v2.mp3"), "mymp3.mp3"); }).Wait();
public static async Task<Stream> DownloadFile(Uri url)
{
var tcs = new TaskCompletionSource<Stream>();
HttpClient http = new System.Net.Http.HttpClient();
HttpResponseMessage response = await http.GetAsync(url);
MemoryStream stream = new MemoryStream();
ulong length = 0;
response.Content.TryComputeLength(out length);
if (length > 0)
await response.Content.WriteToStreamAsync(stream.AsOutputStream());
stream.Position = 0;
return stream;
}
public static async Task<string> DownloadFileFromWeb(Uri uriToDownload, string fileName)
{
using (Stream stream = await DownloadFile(uriToDownload))
{
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await local.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
stream.Position = 0;
using (Stream fileStream = await file.OpenStreamForWriteAsync())
{
stream.CopyTo(fileStream);
}
return file.Path;
}
}

Using statement in Task

In an ASP.NET Web API controller I want to return an image. For streaming the image I need a MemoryStream. Normally I would wrap it in a using statement in order to make sure it gets properly disposed afterwards. However, as this executes asynchronously in a Task this doesn't work:
public class ImagesController : ApiController
{
private HttpContent GetPngBitmap(Stream stream)
{
var pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Save(stream);
stream.Seek(0, SeekOrigin.Begin);
return new StreamContent(stream);
}
// GET api/images
public Task<HttpResponseMessage> Get(string id, string path)
{
//do stuff
return Task.Factory.StartNew(() =>
{
var stream = new MemoryStream(); //as it's asynchronous we can't use a using statement here!
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = GetPngBitmap(stream)
};
response.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
return response;
}
//how can I dispose stream???
});
}
}
MemoryStream is one of the classes that implement IDisposable, because their base class does. MemoryStream doesn't hold any resources (apart from managed memory), so disposing it is actually unnecessary.
HttpResponseMessage is disposable. This means that when the whole response it sent, that object is disposed. Doing that disposes the contained HttpContent, which in the case of StreamContent disposes the underlying Stream. So even if you had a Stream that should be disposed, you don't have to worry about it in this case.
use "using" then it will disposed automatically.
public class ImagesController : ApiController
{
// GET api/images
public Task<HttpResponseMessage> Get(string id, string path)
{
//do stuff
return Task.Factory.StartNew(() =>
{
using( stream = new MemoryStream()) //as it's asynchronous we can't use a using statement here!
{
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = GetPngBitmap(stream)
};
response.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
return response;
}
//how can I dispose stream???
}
});
}
}
Maybe return a Tuple<HttpResponseMessage, Stream> and attach a continuation task to dispose of the Stream?
public Task<Tuple<HttpResponseMessage,Stream>> Get(string id, string path)
{
//do stuff
return Task.Factory.StartNew(() =>
{
var stream = new MemoryStream();
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = GetPngBitmap(stream)
};
response.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
return Tuple.Create(response, stream);
}
})
.ContinueWith(t => t.Result.Item2.Close());
}
Maybe this sounds naif, but can't you just dispose it at the end of the task?
var stream = new MemoryStream();
//use the stream...
stream.Dispose();
edit: or "Close()", it's the same on MemoryStream.
You can do it like this. This will execute your initialRun first and after the async call, return in and it will execute the continue with.
Task<System.IO.MemoryStream> initialRun = new Task<System.IO.MemoryStream>(() =>
{
System.IO.MemoryStream stream = new System.IO.MemoryStream();
// use stream
return stream;
});
initialRun.ContinueWith(new Action<Task<System.IO.MemoryStream>>((stream) =>
{
stream.Dispose();
}),
TaskScheduler.FromCurrentSynchronizationContext());
initialRun.Start();

Categories