2012-04-04 21 views
4

イベント非同期パターンでAsync CTPを使用したことから、がブロックされ、clientGetFileList.GetCompletedが発生するまでコードが正常に動作するはずです。しかし、何が起きるのかは、私がGetRestoreStreamに戻ってきて、return GetRestoreStreamAwait().Resultに戻ってこないということです。代わりに、私のアプリは私にかなりロックされています。ライブSDKでasync/awaitを使用する

誰かが私に間違っていることを説明してもらえますか?

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 
} 

更新:コードのこのビットは、すべての道を移動するようだが、私は実際に最後のストリームを取得することはありませんInvalidOperationExceptiontask.Start()オフに投げます。 try/catchでそれをラップしても、try/catchなしでは、その結果が使用されないという事実に無関係に、操作がうまく動作している間にスタックの上位にキャッチされます。それで、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

を試してみてくださいMicrosoftはすでに持っているようですLive Connect SDKの 'async' /' await'の例、私のGoogle-fuはいくつかの作業が必要です。サンプルはhttps://github.com/liveservices/LiveSDK/tree/master/Samples/WindowsPhone/LiveSDKAsyncAwaitSampleにあり、私が試したこととは多少異なることがあります。 –

答えて

3

あなたは、async/awaitの仕組みを誤解しています。基本的に、あなたのコードはvar result1以下でブロックされています。しかし、待たされているのは、非同期メソッド(この場合はGetRestoreStream)を呼び出したコードが、その前にawaitという長時間実行されているタスク*が呼び出されるとすぐに返されることです。 .Resultに頼らない場合は、GetRestoreStreamメソッドが完了します。ただし、GetRestoreStreamAwaitが完了するのを待つ間、結果が必要なので、GetRestoreStreamメソッドは同期します。まもなくビジュアルを追加します。ここで

は、いくつかのサンプルコードフローである:

-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も非同期メソッドになるように呼び出しをネストする必要があります。しかし、結果が最終的に探しているものであれば、リターンを待つか、通常は非同期パターンの場合と同じようにコールバックを設定する必要があります。

*コンパイラは、(それが実際のawaitが呼び出された時点で終了している場合)は、すでに完成されているコードを書き換え時間を無駄にしません

UPDATE ...この

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

私が読んだところでは、タスクオブジェクトは「ホット」で返され、 'Start'が複数回呼び出されると例外がスローされます。また、私がやっていることは、私が作業している親クラスの要件のために、EAP非同期メソッドのチェーンを同期型メソッドにすることです。 –

+0

これは、すべてのものがそこにどれくらい置かれても、最初の非同期タスクは決して終わっていないようです。私は 'result1'が満たされ、' d1'がunhookedになるという点に決して到達しません。私は次のイベントでも同じケースになると思っていますが、エミュレータとデバッガをどれだけ長く走らせても、その時点までは得られませんでした。 –

+0

@ChrisCharabaruk私はTrySetResultに精通していません。私はそれがあなたがやっているようなものを使うことができることを知っていますが、私は確信が持てません。定期的なGetのためにタスク内でコードをラップし、それが戻ったかどうかを調べることができますか?私の推測では、この非同期呼び出しは正しく構成されておらず、返されるまで回転していません。多分、通常のgetはエラーを投げるだろうか? –

関連する問題