从我所看到的有关将 Async CTP 与事件异步模式一起使用的情况来看,我这里的代码应该可以正常工作,var result1 = await tcs1.Task
阻塞直到 clientGetFileList .GetCompleted
触发。然而,最终发生的事情是,我在 return GetRestoreStreamAwait().Result
时被反弹回 GetRestoreStream,但它永远不会返回——相反,我的应用程序几乎锁定了我。
有人可以向我解释我做错了什么吗?
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 及以下阻塞。但是,await 允许调用异步方法(在本例中为 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 也是一个异步方法。但是,如果结果是您最终要寻找的结果,那么您将需要等待返回,或者像通常对异步模式所做的那样简单地设置一个回调
*请注意,我说的是长时间运行的任务,因为编译器不会浪费时间重写已经完成的代码(如果它确实在调用 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;
}
}
关于c# - 在 Live SDK 中使用异步/等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10004165/