Page 45 - MSDN Magazine, June 2018
P. 45

Figure 1 A Comparison of Async Interfaces
well-known interfaces that follow a similar pattern and offer dif- ferent features for the callee to communicate information back to the caller. The table in Figure 1 provides a comparison of the four async interfaces.
In C++ terms, the interfaces can be expressed as shown in Figure 2.
IAsyncAction and IAsyncActionWithProgress can be waited upon to determine when the async method completes, but these interfaces don’t offer any observable result or return value directly. IAsyncOperation and IAsyncOperationWithProgress, on the other hand, expect the Result type parameter to indicate the type of result that can be expected when the async method completes successfully. Finally, IAsyncActionWithProgress and IAsyncOperationWith- Progress expect the Progress type parameter to indicate the type of progress information that can be expected periodically for long-running operations up until the async method completes.
The role of the language, and libraries in the case of C++, is to take care of the mechanics of the async pattern and provide a natural bridge to a language- specific implementation.
There are a few ways to wait upon the result of an async method. I won’t describe them all here as that would turn this into a very long article. While there are a variety of ways to handle async completion, there are only two that I recommend: the async.get method, which performs a blocking wait, and the co_await async expression, which performs a cooperative wait in the context of
Figure 2 The Async Interfaces Expressed in C++ Terms
a coroutine. Neither is better than the other as they simply serve different purposes. Let’s look now at how to do a blocking wait.
As I mentioned, a blocking wait can be achieved using the get method as follows:
IAsyncAction async = Sample::CopyAsync(); async.get();
There’s seldom any value in holding on to the async object and the following form is thus preferred:
Sample::CopyAsync().get();
It’s important to keep in mind that the get method will block the calling thread until the async method completes. As such, it’s not appropriate to use the get method on a UI thread because it may cause the app to become unresponsive. An assertion will fire in unoptimized builds if you attempt to do so. The get method is ideal for console apps or background threads where, for whatever reason, you may not want to use a coroutine.
Once the async method completes, the get method will return any result directly to the caller. In the case of IAsyncAction and IAsyncActionWithProgress, the return type is void. That might be useful for an async method that initiates a file-copy operation, but less so for something like an async method that reads the contents of a file. Let’s add another async method to the example:
struct Sample {
Sample() = delete;
static Windows::Foundation::IAsyncAction CopyAsync();
static Windows::Foundation::IAsyncOperation<hstring> ReadAsync(); };
In the case of ReadAsync, the get method will properly forward the hstring result to the caller once the operation completes:
Sample::CopyAsync().get();
hstring result = Sample::ReadAsync().get();
Assuming execution returns from the get method, the resulting string will hold whatever value was returned by the async method upon its successful completion. Execution may not return, for example, if an error occurred.
The get method is limited in the sense that it can’t be used from a UI thread, nor does it exploit the full potential of the machine’s concurrency, since it holds the calling thread hostage until the async method completes. Using a coroutine allows the async method to complete without holding such a precious resource captive for some indeterminate amount of time.
Handling Async Completion
Now that you have a handle on async interfaces in general, let’s begin to drill down into how they work in a bit more detail. Assuming you’re not satisfied with the blocking wait provided by the get method, what other options are there? We’ll soon switch gears and focus entirely on coroutines but, for the moment, let’s take a closer look at those async interfaces to see what they offer. Both the coroutine support, as well as the get method I looked at previously, rely on the contract and state machine implied by those interfaces. I won’t go into too much detail because you really don’t need to know all that much about these, but let’s explore the basics so they’ll at least be familiar if you do ever have to dive in and use them directly for something more out of the ordinary.
Name
Result
Progress
IAsyncAction
No
No
IAsyncActionWithProgress
No
Yes
IAsyncOperation
Yes
No
IAsyncOperationWithProgress
Yes
Yes
namespace Windows::Foundation {
struct IAsyncAction;
template <typename Progress> struct IAsyncActionWithProgress;
template <typename Result> struct IAsyncOperation;
template <typename Result, typename Progress>
struct IAsyncOperationWithProgress; }
msdnmagazine.com
June 2018 39


































































































   43   44   45   46   47