2012-04-04 117 views
4

從我所看到的有關使用具有事件異步模式的Async CTP中看到的代碼我應該可以正常工作,var result1 = await tcs1.Task會阻止,直到clientGetFileList.GetCompleted被觸發。然而,最終發生的是我得到彈回到GetRestoreStream,return GetRestoreStreamAwait().Result永遠不會返回 - 相反,我的應用程序幾乎鎖定了我。通過Live SDK使用異步/等待

有人可以請向我解釋我做錯了什麼?

protected override Stream GetRestoreStream() 
{ 
    if (SkyDriveFolderId != null) 
     return GetRestoreStreamAwait().Result; 

    return Stream.Null; 
} 

private async Task<Stream> GetRestoreStreamAwait() 
{ 
    LiveConnectClient clientGetFileList = new LiveConnectClient(_session); 
    TaskCompletionSource<LiveOperationCompletedEventArgs> tcs1 = new TaskCompletionSource<LiveOperationCompletedEventArgs>(); 
    EventHandler<LiveOperationCompletedEventArgs> d1 = (o, e) => { tcs1.TrySetResult(e); }; 

    clientGetFileList.GetCompleted += d1; 
    clientGetFileList.GetAsync(SkyDriveFolderId + "/files"); 
    var result1 = await tcs1.Task; 
    clientGetFileList.GetCompleted -= d1; 

    // ... method continues for a while 
} 

更新:此代碼位似乎看透一路移動,但task.Start()扔關的InvalidOperationException所以我從來沒有真正得到末尾的流。將它封裝在try/catch中並不會改變任何內容 - 如果沒有try/catch,則InvalidOperationException將被進一步捕獲到堆棧中,而操作愉快地忽略了它的結果永遠不會被使用;有了它,task.Result凍結的東西就像上面的代碼一樣肯定。

protected override Stream GetRestoreStream() 
{ 
    if (SkyDriveFolderId != null) 
    { 
     var task = GetRestoreStreamImpl(); 
     task.Start(); 
     return task.Result; 
    } 

    return Stream.Null; 
} 

private async Task<Stream> GetRestoreStreamImpl() 
{ 
    var getResult = await GetTaskAsync(SkyDriveFolderId + "/files"); 

    List<object> data = (List<object>)getResult["data"]; 
    foreach (IDictionary<string, object> dictionary in data) 
    { 
     if (dictionary.ContainsKey("name") && (string)dictionary["name"] == BackupFileName) 
     { 
      if (dictionary.ContainsKey("id")) 
      { 
       SkyDriveFileId = (string)dictionary["id"]; 
       break; 
      } 
     } 
    } 

    if (String.IsNullOrEmpty(SkyDriveFileId)) 
    { 
     MessageBox.Show("Restore failed: could not find backup file", "Backup", MessageBoxButton.OK); 
     return Stream.Null; 
    } 

    return await DownloadTaskAsync(SkyDriveFileId + "/content"); 
} 

private Task<IDictionary<string,object>> GetTaskAsync(string path) 
{ 
    var client = new LiveConnectClient(_session); 
    var tcs = new TaskCompletionSource<IDictionary<string, object>>(); 

    client.GetCompleted += (o, e) => 
     { 
      if (e.Error != null) 
       tcs.TrySetException(e.Error); 
      else if (e.Cancelled) 
       tcs.TrySetCanceled(); 
      else 
       tcs.TrySetResult(e.Result); 
     }; 
    client.GetAsync(path); 
    return tcs.Task; 
} 

private Task<Stream> DownloadTaskAsync(string path) 
{ 
    var client = new LiveConnectClient(_session); 
    var tcs = new TaskCompletionSource<Stream>(); 

    client.DownloadCompleted += (o, e) => 
     { 
      if (e.Error != null) 
       tcs.TrySetException(e.Error); 
      else if (e.Cancelled) 
       tcs.TrySetCanceled(); 
      else 
       tcs.TrySetResult(e.Result); 
     }; 
    client.DownloadAsync(path); 
    return tcs.Task; 
} 
+0

看起來,微軟已經有一個Live Connect SDK的'async' /'await'示例,並且我的Google-fu需要一些工作。該示例位於https://github.com/liveservices/LiveSDK/tree/master/Samples/WindowsPhone/LiveSDKAsyncAwaitSample,並且與我嘗試的方式有所不同。 – 2012-04-05 01:30:22

回答

3

您誤解了async/await的工作方式。基本上,你的代碼在var result1和下面被阻塞。但是,等待允許的是,調用異步方法(本例中爲GetRestoreStream)的代碼在調用前面的await長時間運行的任務*後立即返回。如果你不依賴於.Result,那麼你的GetRestoreStream方法就完成了。但是,由於您需要結果,因此您的GetRestoreStream方法在等待GetRestoreStreamAwait完成時變爲同步。我會盡快添加一些視覺效果。

下面是一些示例代碼流:

-GetRestoreStream calls GetRestoreStreamAwait 
---GetRestoreStreamAwait calls an async task 
-GetRestoreStreamAwait returns to GetRestoreStream with a pending result 
-GetRestoreStream can do anything it wants, but if it calls for the pending result, it will block 
---GetRestoreStreamAwait finally finishes its async task and continues through its code, returning a result 
-Any code in GetRestoreStream that was waiting for the result receives the Result 

這是不是最好的圖形表示,希望這有助於解釋它雖然有點。需要注意的是,由於異步的本質,代碼流並不是你所習慣的

所以,我的猜測是,你的應用程序只能鎖定,因爲你試圖訪問尚不可用的Result ,你所需要做的就是等待tcs1.Task完成。如果你想避免鎖定,那麼你需要嵌套這個調用,這樣GetRestoreStream也是一個異步方法。但是,如果結果是你最終尋找的,那麼你將需要等待回報,或者簡單地設置一個回調,就像你通常會爲異步模式設置回調

*請注意,我說長時間運行的任務,因爲編譯器不會浪費時間重寫一個已經完成

UPDATE(如果它確實是由AWAIT被調用的時候完成)代碼...試試這個

protected override Stream GetRestoreStream() 
{ 
    if (SkyDriveFolderId != null) 
     return GetRestoreStreamAwait().Result; 

    return Stream.Null; 
} 

private async Task<Stream> GetRestoreStreamAwait() 
{ 

    try 
    { 
    LiveConnectClient clientGetFileList = new LiveConnectClient(_session); 
    TaskCompletionSource<LiveOperationCompletedEventArgs> tcs1 = new TaskCompletionSource<LiveOperationCompletedEventArgs>(); 
    EventHandler<LiveOperationCompletedEventArgs> d1 = 
     (o, e) => 
      { 
       try 
       { 
        tcs1.TrySetResult(e); 
       } 
       catch(Exception ex) 
       { 
        tcs1.TrySetResult(null); 
       } 
      }; 

    clientGetFileList.GetCompleted += d1; 
    clientGetFileList.GetAsync(SkyDriveFolderId + "/files"); 
    var result1 = await tcs1.Task; 
    clientGetFileList.GetCompleted -= d1; 

    // ... method continues for a while 
    } 
    catch(Exception ex) 
    { 
     return null; 
    } 
} 
+0

從我讀過的內容來看,任務對象應該返回「hot」,並且如果多次調用「Start」,就會拋出異常。另外,我想要做的是將EAP異步方法鏈變爲同步方法,這是由於父類對我正在工作的一個類的要求。 – 2012-04-04 02:55:52

+0

事情不再是我讓所有事情都坐在那裏多久,那第一個異步任務似乎從未完成。我從來沒有達到'result1'被填滿和'd1'被解除的地步。我認爲下一個事件也會發生同樣的情況,但是無論我讓仿真器和調試器運行了多久,我都從未得到過這一點。 – 2012-04-04 02:57:39

+0

@ChrisCharabaruk我不太熟悉TrySetResult。我知道它可以像你一樣使用,但我無法確定。你可以試着將代碼包裝在一個常規Get的任務中,看看它是否返回。我的猜測是,這種異步調用只是沒有配置正確,並旋轉,直到它得到一個回報....它永遠不會。也許一個普通的get會拋出一個錯誤? – 2012-04-04 03:03:51