使用C++ Coroutines技术简化UWP的异步代码

[原文发表地址] Using C++ Coroutines to simplify async UWP code

[原文发表时间] 2016/4/4

通用windows平台(UWP)引入了许多异步API,目前已经有近1700个API。每个API之间的切换只需要50毫秒左右即可完成异步模式。

编写异步模式的代码并不是轻松的事情, 尤其在C++中必须创建一个PPL任务并且在lambdas表达式中使用continuation (.then)。 实际上许多情况代码本身并不难写, 只不过阅读性不佳。

C++ Coroutines技术可以简化编写的异步代码, 使代码可读性提高,易于维护编写。 下面我们直接看实例:

这段代码中我们尝试打开图片,使用 PickSingleFileAsync和OpenAsync:

 

void AsyncDemoForBuild::MainPage::PickImageClick(Platform::Object^ sender,

       Windows::UI::Xaml::RoutedEventArgs^ e)

{

       using namespace Windows::UI::Xaml::Media::Imaging;

       using namespace Windows::Storage::Pickers;

       using namespace concurrency;

 

       auto picker = ref new FileOpenPicker();

       picker->FileTypeFilter->Append(L".jpg");

       picker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;

       create_task(picker->PickSingleFileAsync()).then([this]

              (Windows::Storage::StorageFile^ file)

       {

              if (nullptr == file)

                     return;

                     create_task(file->OpenReadAsync()).then([this]

                     (Windows::Storage::Streams::IRandomAccessStreamWithContentType^ stream)

              {

                     auto bitmap = ref new BitmapImage();

                     bitmap->SetSource(stream);

                     theImage->Source = bitmap;

                     OutputDebugString(L"1. End of OpenReadAsync lambda.\r\n");

              });

              OutputDebugString(L"2. End of PickSingleFileAysnc lambda.\r\n");

       });

       OutputDebugString(L"3. End of function.\r\n");

}

代码由于需要运行在异步模式而变得复杂。 如果使用同步模式, 就很方便阅读。

// 伪代码

Void ShowImage()

 

{

 

       auto picker = ref new FileOpenPicker();

 

       picker->FileTypeFilter->Append(L”.jpg”);

 

       picker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;

 

       auto file = picker->PickSingleFile();

 

       auto stream = file->OpenRead();

 

       auto bitmap = ref new BitmapImage();

 

       bitmap->SetSource(stream);

 

       theImage->Source = bitmap;

 

}

有了Coroutines, 我们可以使用co_await关键字, 我们还需要做点事情,改写代码为:

 

#include <experimental\resumable>

 

#include <pplawait.h>

 

using namespace Platform;

task<void> AsyncDemoForBuild::MainPage::PickAnImage()

{

       auto picker = ref new FileOpenPicker();

       picker->FileTypeFilter->Append(L".jpg");

       picker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;

 

       auto file = co_await picker->PickSingleFileAsync();

       if (nullptr == file)

              return;

 

       auto stream = co_await file->OpenReadAsync();

 

       auto bitmap = ref new BitmapImage();

       bitmap->SetSource(stream);

       theImage->Source = bitmap;

       OutputDebugString(L"1. End of OpenReadAsync lambda.\r\n");

}

然后这样调用:

 

void AsyncDemoForBuild::MainPage::PickImageClick(Platform::Object^ sender,

Windows::UI::Xaml::RoutedEventArgs^ e)

{

       PickAnImage();

}

这段代码就变得简单多了, UWP C++代码通过使用co_await关键字,变得易于编写, 几乎和同步模式的代码相差不多。 我们也会发现co_await同样适用于C++/CX代码, 也就是说你可以使用^引用, 并且这样不会带来二义性。

使用时也要注意在编译选项中使用/await开关:

需要重点提示在VS2015 U2版本中,你也可以使用/SDL选项去检查异步模式中隐含的警告。

使用co_await是一种方便使用Coroutines技术的形式。然而,C++ Coroutines可以完成更多的事情, 比如,你可以:

· 在使用已有的Coroutines类型的环境中自定义新的awaitables类型

· 定义新的Coroutines类型

关与自定义awaitables类型, 可以参考这篇文章, 我们也有很多关于异步代码为主题的文章。

注意Coroutines还未成为C++标准的一部分, 目前只能在TS(技术规格)中找到, 通过experimental目录进行引用(参考这里)。 由于我们在VS 2015 U2中移除了/RTC和/SDL选项之间的兼容性, 所以我们考虑在产品中使用使用Coroutines。 请告诉我们您的使用情况,以及遇到的一些问题反馈。

Build2016大会也有相关的视频