Eya*_*yal 0 .net vb.net async-await
我的输入是位于Amazon S3服务器上的一长串文件.我想下载文件的元数据,计算本地文件的哈希值,并将元数据哈希值与本地文件的哈希值进行比较.
目前,我使用循环来异步启动所有元数据下载,然后在每个完成时,根据需要在本地文件上计算MD5并进行比较.这是代码(只是相关的行):
Dim s3client As New AmazonS3Client(KeyId.Text, keySecret.Text)
Dim responseTasks As New List(Of System.Tuple(Of ListViewItem, Task(Of GetObjectMetadataResponse)))
For Each lvi As ListViewItem In lvStatus.Items
Dim gomr As New Amazon.S3.Model.GetObjectMetadataRequest
gomr.BucketName = S3FileDialog.GetBucketName(lvi.SubItems(2).Text)
gomr.Key = S3FileDialog.GetPrefix(lvi.SubItems(2).Text)
responseTasks.Add(New System.Tuple(Of ListViewItem, Task(Of GetObjectMetadataResponse))(lvi, s3client.GetObjectMetadataAsync(gomr)))
Next
For Each t As System.Tuple(Of ListViewItem, Task(Of GetObjectMetadataResponse)) In responseTasks
Dim response As GetObjectMetadataResponse = Await t.Item2
If response.ETag.Trim(""""c) = MD5CalcFile(lvi.SubItems(1).Text) Then
lvi.SubItems(3).Text = "Match"
UpdateLvi(lvi)
End If
Next
Run Code Online (Sandbox Code Playgroud)
我有两个问题:
我按照我制作它的顺序等待响应.我宁愿按照他们完成的顺序处理它们,以便让它们更快.
MD5计算是长且同步的.我尝试将其设为异步,但进程已锁定.我认为MD5任务被添加到.Net的任务列表的末尾,并且在完成所有下载之前它没有运行.
理想情况下,我会在响应到达时处理响应,而不是按顺序处理,并且MD5是异步的但有机会运行.
编辑:
合并WhenAll,它现在看起来像这样:
Dim s3client As New Amazon.S3.AmazonS3Client(KeyId.Text, keySecret.Text)
Dim responseTasks As New Dictionary(Of Task(Of GetObjectMetadataResponse), ListViewItem)
For Each lvi As ListViewItem In lvStatus.Items
Dim gomr As New Amazon.S3.Model.GetObjectMetadataRequest
gomr.BucketName = S3FileDialog.GetBucketName(lvi.SubItems(2).Text)
gomr.Key = S3FileDialog.GetPrefix(lvi.SubItems(2).Text)
responseTasks.Add(s3client.GetObjectMetadataAsync(gomr), lvi)
Next
Dim startTime As DateTimeOffset = DateTimeOffset.Now
Do While responseTasks.Count > 0
Dim currentTask As Task(Of GetObjectMetadataResponse) = Await Task.WhenAny(responseTasks.Keys)
Dim response As GetObjectMetadataResponse = Await currentTask
If response.ETag.Trim(""""c) = MD5CalcFile(lvi.SubItems(1).Text) Then
lvi.SubItems(3).Text = "Match"
UpdateLvi(lvi)
End If
Loop
MsgBox((DateTimeOffset.Now - startTime).ToString)
Run Code Online (Sandbox Code Playgroud)
只要MDSCalcFile完成,UI就会暂时锁定.整个循环大约需要45秒,第一个文件的MD5结果在启动后的1秒内发生.
如果我将行更改为:
If response.ETag.Trim(""""c) = Await Task.Run(Function () MD5CalcFile(lvi.SubItems(1).Text)) Then
Run Code Online (Sandbox Code Playgroud)
完成MD5CalcFile后,UI不会锁定.整个循环大约需要75秒,从45秒开始,第一个文件的MD5结果在等待40秒后发生.
EDIT2:
我发现了一个适合我的解决方案.问题出在我的GetObjectMetadataAsync中.我写错了.注释中的错误版本的正确版本如下:
<System.Runtime.CompilerServices.Extension>
Function GetObjectMetadataAsync(a As AmazonS3Client, l As GetObjectMetadataRequest) As Task(Of GetObjectMetadataResponse)
Return Task.Factory.FromAsync(AddressOf a.BeginGetObjectMetadata, AddressOf a.EndGetObjectMetadata, l, Nothing)
'Return Task.Run(Function()
' Try
' Return a.GetObjectMetadata(l)
' Catch ex As Amazon.S3.AmazonS3Exception
' If ex.ErrorCode = "NoSuchKey" Then
' Return Nothing
' Else
' Throw ex
' End If
' End Try
' End Function)
End Function
Run Code Online (Sandbox Code Playgroud)
我不知道为什么将一个同步版本放入一个线程或使用FromAsync,这很重要,但显然后者看起来更好,测试显示它要快得多.
您可以WhenAny在完成任务结果时使用它们来处理:
while (responseTasks.Length > 0)
{
var completedTask = await Task.WhenAny(responseTasks);
responseTasks.Remove(completedTask);
var response = await completedTask;
...
}
Run Code Online (Sandbox Code Playgroud)
(对不起,对于C#来说,我的VB语法是正确的太长了).
有关该主题的完整讨论,请参阅Stephen Toub关于该主题的帖子.
另一个选项是TPL Dataflow,它允许您为数据构建"网格".对于此示例,Dataflow可能过度,但如果您的实际处理更复杂,它将非常有用.
就MD5而言,使其异步应该不是问题.基于异步I/O的任务(例如返回的任务GetObjectMetadataAsync)不使用线程池线程.我会尝试其他一些场景(比如自己异步运行MD5)然后发布另一个问题,如果没有明显的问题.