通过Live SDK使用异步/等待
从我所看到的有关使用具有事件异步模式的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;
}
您误解了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;
}
}
从我读过的内容来看,任务对象应该返回“hot”,并且如果多次调用“Start”,就会抛出异常。另外,我想要做的是将EAP异步方法链变为同步方法,这是由于父类对我正在工作的一个类的要求。 – 2012-04-04 02:55:52
事情不再是我让所有事情都坐在那里多久,那第一个异步任务似乎从未完成。我从来没有达到'result1'被填满和'd1'被解除的地步。我认为下一个事件也会发生同样的情况,但是无论我让仿真器和调试器运行了多久,我都从未得到过这一点。 – 2012-04-04 02:57:39
@ChrisCharabaruk我不太熟悉TrySetResult。我知道它可以像你一样使用,但我无法确定。你可以试着将代码包装在一个常规Get的任务中,看看它是否返回。我的猜测是,这种异步调用只是没有配置正确,并旋转,直到它得到一个回报....它永远不会。也许一个普通的get会抛出一个错误? – 2012-04-04 03:03:51
看起来,微软已经有一个Live Connect SDK的'async' /'await'示例,并且我的Google-fu需要一些工作。该示例位于https://github.com/liveservices/LiveSDK/tree/master/Samples/WindowsPhone/LiveSDKAsyncAwaitSample,并且与我尝试的方式有所不同。 – 2012-04-05 01:30:22