I am fairly new to C#. I have been a C++ programming using MFC and other similar frameworks for the last 20 years, but the switch to c# has been fairly easy. Computer languages are like hammers; you can go to the store and buy a bigger one or a smaller one, or one in a different color, but they are all still the same tool doing the same job. That is, until you add streams, asynchronous programming, and other features, that are part of the application framework, not the computer language.
“An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. Control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off.”
The asynchronous programming paradigm has given me trouble for the last few days. I am porting Windows Phone code to a WinRT app and reading and writing files is a pain in the butt. IN the old days, code looked like this (Pseudo-code follows):
Write Bytes to File;
The file close is not really needed when using a file object because the object destructor will close the file. That reduces this down to two lines of code. Sure, there should be some error checking. The point is, it’s not a lot of code. Code in C# for WinRT, if a synchronous file write is needed, will look something like this:
Open File in Local Storage;
Wait for That Open Operation to Finish;
Create Write Stream;
Wait for That Write Stream Operation to Finish;
Write Bytes to Stream;
Wait for That Write Operation To Finish;
There is no file close operation. I’m still trying to understand the reasoning for the missing Close() method.
The problem for the programming is that every line of code requires time and money to write and also time and money to maintain. More lines of code adds up to more costs. Every line is, of course, a potential bug.
It was not until today when I read that “An await expression does not block…” quote on the linked page, that I understood what’s going on. The await expression is really a return expression that tells the compiler to start the given method (no one calls them functions anymore?) in a background thread of some sort, and then return to the caller. The code within the same outer method, that comes after the await expression, is queued in some way to run after the awaited method completes.
It’s all very clever. Clever is also expensive to maintain.
Here is a real-world example of some of the code that I’m porting at the moment. It’s not done, but this first pass shows what’s going on:
public async Task AddPhoto( WriteableBitmap Bitmap )
string MyFile = GetNewPhotoFileName();
// Create a file and open a stream to it, all synchronously. After all, this code is already in a non-UI thread.
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(MyFile, CreationCollisionOption.ReplaceExisting);
IRandomAccessStream filestream = await file.OpenAsync(FileAccessMode.ReadWrite);
BitmapEncoder encoder = await BitmapEncoder.CreateAsync( BitmapEncoder.JpegEncoderId, filestream );
Here is the first chunk of code within a method that saves a WriteableBitmap object to a file. The AddPhoto method is marked as being asynchronous. This means that the caller must also be asynchronous or it must not have an await operator applied to the method call. I’m still trying to figure out the best way to deal with this because calling one of these asynchronous methods without an await, and without getting the task object that is returned from the function, produces a warning message when the code is compiled.
Anyhow, a call to this method results in the code returning execution to the caller at the first await operation. That CreateFileAsync() call runs in some other thread. All of the code in this method after that line (7) is scheduled to run in the background thread after the CreateFileAsync() call is finished. Each subsequent await operation works in the same way.
This could be written as synchronous code by getting back the Task object that is returned by each async method and then using it to wait for the method to complete before executing more code.
One final thought is that most of my code performing this type of function is already in a background thread. There should have been a way for the compiler to know this or for me to tell it this. Then all of the calls could have been synchronous and easier to code. The only reason to ever return to the caller immediately is if the caller happens to be code executing in the user interface thread.
I am still learning this and some of this post information might be incorrect. I don’t ever expect to be an expert, but I do hope to be proficient at writing code that works correctly. Now I just need to create my own synchronous file handling class to bypass all of this async stuff.
And don’t get me started on all of this stream crap. I don’t care about streams, I just want to open a file and write to it. Adding the create-stream step is just a waste of space in the source code.