C++異步編程 for VS2011(四)


在這一章中,我們討論一下如何創建和使用一個異步的WinRT API。

 

在WinRT中有四種異步的接口,IAsyncAction 沒有返回值類型的接口,IAsyncActionWithProgress<TProgress> 沒有返回值類型,但是有進度監視器的接口。IAsyncOperation<TResult> 返回值為T的接口,IAsyncActionOperationWithProgress<TResult,TProgress> 返回值為T,有進度監視器的接口。

 

 之前我們說過,在WinRT中可以使用task來調用IAsyncAction 等接口,但是有一些其他的功能被限制,比如:

1. 在WinRT中task只能用.then來加入前面的task執行成功之后的行為。task.wait函數不可以使用。

2. progress_reporter不能在task里面使用。

3. cancellation_taken,可以作為傳入參數在task里面使用。

cancellation_token_source fileTaskTokenSource;

//  Cancel button event handler:
fileTaskTokenSource.cancel();

//  task chain

task<StorageFile^> getFileTask(storageFolder->GetFileAsync(), fileTaskTokenSource.get_token());

 

4. 錯誤的捕獲要在.then的方法里面進行

    StorageFolder^ documentsFolder = KnownFolders::DocumentsLibrary;
    task<StorageFile^> getFileTask(documentsFolder->GetFileAsync(fileName));

    getFileTask.then([](StorageFile^ storageFileSample) {       
         return storageFileSample->DeleteAsync();
    }).then([](task< void> t) {

         try
        {
            t. get();

             //  .get() didn't throw, so we succeeded.
            OutputDebugString(L " File deleted. ");
        }
         catch (Exception^ e)
        {
                  // Example output: The system cannot find the specified file.
                 OutputDebugString(e->Message->Data());
        }
        

    });

 

 上面的部分說了一些在用IAsyncAction的注意事項。接着我們講講如何寫IAsyncAction等方法。

我們需要用 create_async去創建IAsyncAction等返回值類型的異步操作。關於create_async的傳入參數和返回值,可以參照下面的表格

To create this Windows Runtime interface

Return this type fromcreate_async

Pass these parameter types to your work function to use an implicit cancellation token

Pass these parameter types to your work function to use an explicit cancellation token

IAsyncAction

void ortask<void>

(none)

(cancellation_token)

IAsyncActionWithProgress<TProgress>

void ortask<void>

(progress_reporter)

(progress_reporter,cancellation_token)

IAsyncOperation<TResult>

T or task<T>

(none)

(cancellation_token)

IAsyncActionOperationWithProgress<TResult
s, TProgress>

T or task<T>

(progress_reporter)

(progress_reporter,cancellation_token)

 

這里順便要說的,create_async可以返回task<void>或者直接返回void,返回void的時候,lambda表達式里面的代碼會在后台執行,同時這個async的方法可以被其他語言調用。當我們這段表達式里面含有異步操作的時候,我們就要用返回task,比如

IAsyncOperation<T>^ asyncOperation = create_async( []() {
     return create_task(FirstAsync(...))
    .then( [](X val){
         return SecondAsync(val, ...);
    }).then( [](Y val)
         return ThirdAsync(val, ...);
    });

});

這段代碼會執行FirstAsync->SecondAsync->ThirdAsync,最后返回的是 ThirdAsync的結果。

 

我們可以創建一個有返回值,有取消操作,有進度監視器,可以拋出錯誤的 IAsyncActionOperationWithProgress 函數如下

IAsyncOperationWithProgress< int,String^>^ TestWithProgressAsync( int input)
{
     return create_async([input](progress_reporter<String^> reporter) {
         if(input== 0)
              throw  ref  new InvalidArgumentException();
         bool moreToDo =  true;
         while (moreToDo)
        {
             //  Check for cancellation.
             if (is_task_cancellation_requested()) {                
                 //  Cancel the current task.
                cancel_current_task();
                 return  32;
                moreToDo =  false;
            }
             else {
                 //  Perform work.
                reporter.report( " Running ");
            }
        }
        reporter.report( " Finished ");
         return  42;
    });

 

progress_reporter<T> reporter 作為傳入參數,這個T可以是各種類型,[input] 是為了綁定傳入參數,is_task_cancellation_requested()捕獲取消事件, reporter.report(T)匯報進度。

 

接着我們演示在WinRT中如何使用這個異步函數,使用這個異步函數有兩種方式,一是通過task 封裝,就如同文章開頭所說,二是通過調用純WinRT接口。

首先,我們演示一下如何用task的方式去調用這個異步函數。

首先,聲明和初始化一個task和cancellation

 // In BlankPage.xaml.h

        cancellation_token_source TaskTokenSource;
        task< int> t;

//  In BlankPage.xaml.cpp

BlankPage::BlankPage()
{
    InitializeComponent();
     //  TestWithProgressAsync(0) will throw a execption
    t=task< int>(TestWithProgressAsync( 1),TaskTokenSource.get_token());
}

 

接着,拖一個cancel和except按鈕還有一個名字是result 的TextBlock,在兩個按鈕的click事件中寫入:

 void Application3::BlankPage::Button_Click_Cancel(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)

{
    
    
    cancellation_token_registration cookie;
    auto token =TaskTokenSource.get_token();
     cookie = token.register_callback([ this, token, &cookie]() {
         safe_cast<TextBlock^>( this->FindName( " result "))->Text+= " Cancel ";
         //  Although not required, demonstrate how to unregister 
        
//  the callback.
        token.deregister_callback(cookie);
    });
    TaskTokenSource.cancel();
}


void Application3::BlankPage::Button_Click_Except(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
     int c= 0;
    t.then([ this,&c](task< int> t){
         try
        {
        
             int res=t. get();

    c+=res;   

            safe_cast<TextBlock^>(this->FindName("result"))->Text+=res;
        }catch(Exception^ ex)
        {
            safe_cast<TextBlock^>(this->FindName("result"))->Text+=ref new String(ex->Message->Data());
        }
    },task_continuation_context::use_arbitrary());
}

 

這里要注意的幾點是:

1. task里面無法使用進度監視器。

2. 我們可以通過注冊token的callback 函數來處理cancel之后的行為,在不改變原始函數的情況下。上面的例子中,我們用callback函數來修改TextBlock里面的內容。

3. 我們只有在 t.then([this](task<int> t) 這種情況下才可以使用t.get(),如果我們直接使用Application3::BlankPage::t.get()會報錯。

4. 如果我們在想使用.then 外面的變量,比如Button_Click_Except函數里面的 int c,我們不僅僅需要綁定[this,&c](task<int>, 我們還需在then方法里面加入task_continuation_context::use_arbitrary()這個參數。這是因為XAML的UI線程都是STA的,而Lambda表達式是MTA,所以兩者之間共享數據默認是不可以的。所以就需要task_continuation_context::use_arbitrary()這個參數來修改這個默認行為。

 

我們再來看看如何用WinRT的namespace里面的接口來調用這個異步函數。

首先,要聲明一個 Windows::Foundation::IAsyncOperationWithProgress<int,Platform::String^>^ AsyncOper; 在頭文件里,這里注意如果沒有引用Platform命名空間的話,一定要在Sting^前面加上這個命名空間,否則編譯會報錯。

 

然后在cpp文件里

 BlankPage::BlankPage()

{
    InitializeComponent();
     //  TestWithProgressAsync(0) will throw a execption
    AsyncOper=TestWithProgressAsync( 0);

    AsyncOper->Progress= ref  new AsyncOperationProgressHandler< int,String^>(
        [ this](IAsyncOperationWithProgress< int,String^>^ pretask,  String^ progressInfo){
            safe_cast<TextBlock^>( this->FindName( " result "))->Text+=progressInfo+ " \n ";

    });

    AsyncOper->Completed= ref  new AsyncOperationWithProgressCompletedHandler< int,String^>(
        [ this](IAsyncOperationWithProgress< int,String^>^ pretask, AsyncStatus asyncStatus){
             if(asyncStatus==AsyncStatus::Completed)
            {
                 int res=pretask->GetResults();
            }
             if(asyncStatus==AsyncStatus::Canceled)
            {
                safe_cast<TextBlock^>( this->FindName( " result "))->Text= " Cancel ";
            }
             if(asyncStatus==AsyncStatus::Error)
            {
                 try
                {
                     int res=pretask->GetResults();

                } catch(Exception^ ex)
                {
                    safe_cast<TextBlock^>( this->FindName( " result "))->Text+= ref  new String(ex->Message->Data());
                }
            }
    });
}


void Application3::BlankPage::Button_Click_Cancel(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    AsyncOper->Cancel();
}

 

我們通過AsyncOper->Progress=ref new AsyncOperationProgressHandler 來添加進度監視器

通過AsyncOper->Completed=ref new AsyncOperationWithProgressCompletedHandler 添加完成事件的委托,然后我們判斷asyncStatus的三個狀態,來做出不同的反應。

通過AsyncOper->Cancel()來取消異步操作。

這里面要強調的是錯誤的捕獲要用類似於task的方法,既try-catch塊。IAsyncInfo.ErrorCode 這個屬性得到的值是未初始化的。

 

最后,本文介紹了如何寫和如何調用一個WinRT的異步操作,希望這篇文章能給大家在使用WinRT異步API的時候提供一些幫助。 

 

引用自:

http://msdn.microsoft.com/en-us/magazine/hh781020.aspx 

http://msdn.microsoft.com/en-us/library/windows/apps/hh780559.aspx 

http://msdn.microsoft.com/en-us/library/windows/apps/hh750082.aspx 

 http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/8061242d-0e96-4d9d-8743-563291ef8088/

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM