Windows 8 异步编程
在Windows 8里面很多API都封装成了异步的形式,因此异步编程成为了Windows 8的一大特色,同时也给Windows 8的应用更好的用户体验和简化了异步编程的复杂度。异步编程在Windows 运行时中是规范,而不是特例。JavaScript、C#、Visual Basic 和 C++ 都各自为异步方法提供了语言支持。
许多 Windows 运行时功能,如 MediaCapture 和 StorageFile,都被公开为异步函数。按照惯例,异步函数的名称以 "Async" 结尾,表示当调用已返回后可能会发生其部分执行。
当你在 Metro 风格应用中使用异步 API 时,你的代码将以一致的方式进行非阻止调用。当你在 API 中实现这些异步模式后,调用者可以理解并按照可预知的方式使用你的代码。
下面是一些需要调用异步 Windows 运行时 API 的常见任务。
-
显示消息对话框。
-
使用文件系统。
-
向 Internet 发送数据和从 Internet 接收数据。
有了 Windows 运行时异步 API 以后,你无需明确管理线程或直接与基础实现进行交互。
每种编程语言都按照其自己的方式支持异步模式:
编程语言 | 异步表示形式 |
---|---|
JavaScript | 承诺对象,then 函数 |
C# | 将来对象,await 运算符 |
Microsoft Visual Basic .NET | 将来对象,Await 运算符 |
Visual C++ | task 类,.then 方法 |
下面来看一下一个获取网络信息的异步编程的例子
C#版本
UI
<Page x:Class="AsyncDemo.MainPage" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AsyncDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Button Content="获取网络信息" HorizontalAlignment="Left" Margin="114,280,0,0" VerticalAlignment="Top" Height="95" Width="210" Click="Button_Click_1"/> <TextBlock x:Name="tb" HorizontalAlignment="Left" Margin="395,148,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Height="473" Width="878"/> </Grid> </Page>
CS页面
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; using Windows.Web.Syndication; namespace AsyncDemo { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { } private async void Button_Click_1(object sender, RoutedEventArgs e) { Windows.Web.Syndication.SyndicationClient client = new SyndicationClient(); client.BypassCacheOnRetrieve = true; Uri feedUri = new Uri("http://feed.cnblogs.com/blog/sitehome/rss"); // 调用异步方法获取网络信息 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri); //下面的代码在异步await 完成之后才开始执行 tb.Text = feed.Title.Text + Environment.NewLine; foreach (SyndicationItem item in feed.Items) { tb.Text += item.Title.Text + ", " + item.PublishedDate.ToString() + Environment.NewLine; } } } }
对异步方法 RetrieveFeedAsync 的调用,行 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri)
使用 await 运算符。
假如使用的是同步方法的时候,UI会一直卡住一直等到网络请求完成之后才会释放掉UI的线程。但如果你调用 client.RetrieveFeedAsync
,则方法启动检索并立即返回。当你将 await 与 RetrieveFeedAsync 结合使用时,应用临时退出事件处理程序。然后,它便可以在 RetrieveFeedAsync 异步执行时处理其他事件。 这样便可以保持应用对用户进行响应。 当 RetrieveFeedAsync 完成并且 SyndicationFeed 可用时,应用一定会在 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri)
之后重新进入它停止的事件处理程序,并完成方法的剩余部分。
返回异步 API 的类型和结果
如果你跟随指向 RetrieveFeedAsync 的链接,那么你可能会注意到 RetrieveFeedAsync 的返回类型不是 SyndicationFeed。 返回类型为 IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress>
。 异步 API 返回一个包含结果的对象。 尽管该对象很常见,但有时却很有用,若要将异步方法视为可等待的方法,await 运算符实际上对该方法的返回值执行操作,而不是对该方法执行操作。 当你应用 await 运算符时,可以获取异步方法的结果。
当使用异步方法时,你可以通过查看返回值来获取有关其结果的信息。 Windows 运行时中的所有异步 API 都返回以下类型之一:
- IAsyncOperation<TResult>
- IAsyncOperationWithProgress<TResult, TProgress>
- IAsyncAction
- IAsyncActionWithProgress<TProgress>
异步方法的结果类型与 TResult
类型参数相同。 没有 TResult
的类型没有结果。 你可以将结果视为 void。 在 Visual Basic 中,Sub 过程等同于返回类型为 void 的方法。
下表给出了异步方法的示例并列出了每个方法的返回类型和结果类型。
异步方法 | 返回类型 | 结果类型 |
---|---|---|
SyndicationClient.RetrieveFeedAsync | IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> | SyndicationFeed |
ContactPicker.PickSingleContactAsync | IAsyncOperation<ContactInformation> | ContactInformation |
XmlDocument.SaveToFileAsync | IAsyncAction | void |
InkStrokeContainer.LoadAsync | IAsyncActionWithProgress<ulong> | void |
RandomAccessStream.CopyAsync | IAsyncOperationWithProgress<ulong,ulong> | ulong |
DataReader.LoadAsync | DataReaderLoadOperation,它实现 IAsyncOperation<uint> | uint |
.NET for Metro style apps 中的异步方法的返回类型为 Task 或 Task<TResult>。 返回 Task 的方法与 Windows 运行时中返回 IAsyncAction 的异步方法类似。 在任何情况下,异步方法的结果都为 void。 同样,返回类型 Task<TResult> 类似于 IAsyncOperation<TResult>,因为异步方法的结果与 TResult
类型参数的类型相同。
下面的是C++的版本
UI
<Page x:Class="AsyncDemoApp.MainPage" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AsyncDemoApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Button Content="获取网络信息" HorizontalAlignment="Left" Margin="114,280,0,0" VerticalAlignment="Top" Height="95" Width="210" Click="Button_Click_1"/> <TextBlock x:Name="tb" HorizontalAlignment="Left" Margin="395,148,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Height="473" Width="878"/> </Grid> </Page>
h头文件
#pragma once #include "MainPage.g.h" namespace AsyncDemoApp { /// <summary> /// An empty page that can be used on its own or navigated to within a Frame. /// </summary> public ref class MainPage sealed { public: MainPage(); protected: virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; private: void Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); }; }
cpp
#include "pch.h" #include <ppltasks.h > #include "MainPage.xaml.h" using namespace AsyncDemoApp; using namespace Platform; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Controls::Primitives; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Navigation; using namespace concurrency; using namespace Windows::Web::Syndication; MainPage::MainPage() { InitializeComponent(); } void MainPage::OnNavigatedTo(NavigationEventArgs^ e) { (void) e; // Unused parameter } void AsyncDemoApp::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { String^ url = "http://feed.cnblogs.com/blog/sitehome/rss"; SyndicationClient^ client = ref new SyndicationClient(); auto feedOp = client->RetrieveFeedAsync(ref new Uri(url)); auto getNetInfoTask = create_task(feedOp); getNetInfoTask.then([this](SyndicationFeed^ feed) { tb->Text = feed->Title->Text; }); }
在 C++ 中直接使用异步 Windows 运行时 API,首选方法是使用 task class 及其相关类型和函数,它们都包含在 concurrency 命名空间中且在 ppltasks.h 中定义。
利用任务类来使用返回 IAsyncOperation 接口且其操作会生成一个值的异步方法,步骤如下:
1调用 create_task 方法并将其传到 IAsyncOperation^ 对象。
2调用任务上的成员函数 task::then 并且提供一个将在异步操作完成时调用的 lambda。
task::then 方法立即返回,并且其委派直至异步工作成功完成后才运行。
运行的效果如下: