随笔 - 55  文章 - 15  trackbacks - 0
<2012年10月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

     这是Windows 8 异步编程的第二部分,为什么分成两节呢,因为,第二节确实太让人兴奋了。恰巧有篇Windows 8 开发人员博客也是这样一个结构,题目是WinRT 和 await,之前看过,没有什么感觉,今天试了一下,里面还是有很多需要注意的地方。

参考文献:
      1. Windows 8 应用程序开发人员博客:深入探究WinRT和await:http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/04/30/winrt-await.aspx 
      2. MSDN

因为我使用的是C++语言,所以我主要是用C++来编程,C#开发的朋友可以看参考文献1,以下是我的理解和我觉得重要的东西。请原谅我有一部分内容是摘抄自文献1.
一。 基础知识
      我也觉得应该从基础着手,WinRT中的所有异步功能全部源自一个接口:Windows::Foundation::IAsyncInfo

1 public interface IAsyncInfo
2 {
3     AsyncStatus Status { get; }//只读
4     HResult ErrorCode { get; }//只读
5     uint Id { get; }//只读
6 
7     void Cancel();
8     void Close();
9 }
      所有的异步操作都应该实现此接口。但是该接口缺少了一个至关重要的功能:操作完成之后的回调函数。因此就有了下面四个接口。我想说,请大家注意每个新街口中都有一个Completed的属性,我们可以设置这个属性所要执行的代码,就是我们的操作完成之后,将要执行的代码。

1
 public interface IAsyncAction : IAsyncInfo
 2 {
 3     AsyncActionCompletedHandler Completed { getset; }
 4     void GetResults();// void,不返回结果
 5 }
 6 
 7 public interface IAsyncOperation<TResult> : IAsyncInfo
 8 {
 9     AsyncOperationCompletedHandler<TResult> Completed { getset; }
10     TResult GetResults();//TResult 返回结果
11 }
12 
13 public interface IAsyncActionWithProgress<TProgress> : IAsyncInfo
14 {
15     AsyncActionWithProgressCompletedHandler<TProgress> Completed { getset; }
16     AsyncActionProgressHandler<TProgress> Progress { getset; }//有Progress
17     void GetResults();//void,不返回结果
18 }
19 
20 public interface IAsyncOperationWithProgress<TResult, TProgress> : IAsyncInfo
21 {
22     AsyncOperationWithProgressCompletedHandler<TResult, TProgress> Completed { getset; }
23     AsyncOperationProgressHandler<TResult, TProgress> Progress { getset; }//有Progress
24     TResult GetResults();//TResult,返回结果
25 }
    
        暂停,我想说,C#开发人员真的很幸运,C++开发人员需要写很久的代码,C#只要一点点就足够了。这是我的牢骚,请忽略。
      那么我们还是以打开文件为例吧,上一篇文章中,我们使用了create_task这个方法,打开了文件,并且将一些内容显示在一个OutputTextBlock中,现在我们不使用task类,只使用我们的IAsyncOperation来做。
      好,下面是C++同学的时间,请问,你能写出completed这个event的代码么?并保证其运行。

那开始吧。要知道FileOpenPicker::PickSingleFileAsync()返回的是一个Windows::Foundation::IAsyncOperation<Windows::Storage::StorageFile^>^类型的操作:
Windows::Foundation::IAsyncOperation<Windows::Storage::StorageFile^>^ operation = openPicker->PickSingleFileAsync();//为了让大家看清楚类型,就没有用auto简写,大家用auto的话会非常简单
      对应上面的四个接口,显然,返回的是第二个接口IAsyncOperation<T TResult>^操作,那么它有一个Windows::Foundation::AsyncOperationCompletedHandler<TResult>^ 类型的Completed事件,那么我们的completed就应该写成这样:
operation->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>(//暂时省略);
      好复杂!
      还没完,继续。AsyncOperationCompletedHandler是一个Delegate,这个Delegate其实就是原来的函数指针封装了一下,既然是函数指针,那么肯定有参数,怎么寻找这个参数呢?把鼠标移到这个方法上,按下F12就会谈到Object Browse里面去,你就会发现这个Delegate,它有一个Invoke(。。。)方法,里面就是它的参数了。
      另外,我们没使用+=操作符,而是使用了=,为什么呢?因为Completed是个属性,不是个event。。。
      回调函数的代码段,我们用Lambda表达式来写:
       1 operation->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>([this](Windows::Foundation::IAsyncOperation<Windows::Storage::StorageFile^>^ asyncInfo, Windows::Foundation::AsyncStatus status) 
 2         {
 3             //这里,貌似程序会在这里执行3次,具体原理不太清楚。
 4             if(status == Windows::Foundation::AsyncStatus::Completed)
 5             {
 6                 //因为执行3次,所以我在这个里面加了一个计数器,但是最终的结果还是1,这个异步方法还是执行了一次
 7                 //这里为什么是要Dispatcher->RunAsync呢,因为,这是一个回调函数,另一个线程中了。
 8                 //可以看到,这所有的一切都可以用then或者await来代替。
 9                 this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this](){
10                     static int i = 0;
11                     i++;
12                     OutputTextBlock->Text = i.ToString();
13                 }));
14             }
15             //如果执行了operation->Canceled()方法会到这里
16             else if(status == Windows::Foundation::AsyncStatus::Canceled)
17             {
18                 this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this](){
19                     OutputTextBlock->Text = "Canceled";
20                 }));
21             }
22             //操作出现错误会到这里
23             else if(status == Windows::Foundation::AsyncStatus::Error)
24             {
25                 this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this](){
26                     OutputTextBlock->Text = "Error";
27                 }));
28             }
29             //这里不能返回StorageFile^ 类型的对象,为神马呢?因为TResult是void,如果你返回了,那么编译器会提醒你很长一段东西,你自己就会发现了
30             //return safe_cast<Windows::Storage::StorageFile^>(asyncInfo->GetResults());
31         });

      具体的内容都是上面的这段代码了,有些东西我想再提醒一下。
1. 首先,我们来看第30行,因为我觉得这个方法是返回一个StorageFile对象的,所以我在Lambda方法中理应返回它,但是如果返回的话,就是错误的,为什么?原因是这里的返回值实际上是Invoke方法的返回值,在这里这个返回值是void的,所以你不能返回任何东西。
2. 使用asyncInfo->GetResults();可以得到你的结果,回头看看那四个接口,有些返回TResult,有些返回void,是吧。
3. 在这个代码段里,我们已经跑到了另外一个线程中去了,如果你想操作UI,那么,你必须使用Dispatcher->RunAsync方法调度回UI线程,不信你可以试试。
4. 你可以在代码中判断现在IAsyncOperation的状态。


       哦,看看C#程序员的优越性把:
op.Completed = (info, status) =>
            {
                if (status == AsyncStatus.Completed)
                {
                    SyndicationFeed feed = info.GetResults();
                    UpdateAppWithFeed(feed);
                }
                else if (status == AsyncStatus.Canceled)
                {
                    // Operation canceled
                }
                else if (status == AsyncStatus.Error)
                {
                    // Error occurred, Report error
                }
            };
       
希望你还能坚持。*^◎^*
       看到这里你发现了么?这段代码的功能其实是跟上一篇代码的功能一模一样,再把之前的代码贴过来
 1 create_task(openPicker->PickSingleFileAsync()).then([this](StorageFile^ file)
 2         {
 3             if (file)
 4             {
 5                 OutputTextBlock->Text = "Picked photo: " + file->Name;
 6             }
 7             else
 8             {
 9                 OutputTextBlock->Text = "Operation cancelled.";
10             }
11         }).then([this](task<void> t)
12         {
13             try{
14                 t.get();
15             }catch(Platform::Exception^ e)
16             {
17                 OutputTextBlock->Text = e->Message
18             }
19         });
    
      额,希望你能明白,task为我们做了些什么东西。我来总结一下吧:
1. 没有了复杂的completed回调函数,操作完成之后,执行then中的代码,简单而明了。
2. then中的异步代码是在UI线程中的,没有Dispatcher->RunAsync方法,不是么?
3. 可以有多个then
4. 可以处理异常,不是更强大么?
      task为我们做的,就是从30多行复杂的代码精简到只有20行。

至于原理,大家可以阅读参考文献1,我暂时还没有怎么理解。。。
posted on 2013-01-06 16:43 Dino-Tech 阅读(1972) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理