2015-08-19 3 views
1

Просто вступайте в асинхронное программирование, и моя конечная цель заключается в том, чтобы асинхронно получить хэши sha1 заданного набора файлов, а затем продолжить обработку указанных файлов с вычисленным значением хэша. Я обнаружил, что следующее сообщение в блоге MSDN говорит о том, как обрабатывать результаты задачи по завершении задач: LinkИспользуйте await без async для обработки задач по мере их завершения.

Вся статья написана на C#, а мой проект - на VB.NET. Я попытался переписать код C# на сам VB, однако мне не хватает критического шага или я не полностью понимаю процесс/синтаксис программирования Async/Await.

Я получаю следующее сообщение об ошибке в следующих строках:

Ошибка 1 «Await» может быть использована только тогда, когда содержится в выражении метода или лямбда-помеченной модификатором «Асинхронный».

Dim t As Task(Of Integer) = Await bucket 
Dim result As Integer= Await t 

Dim t As Task(Of String) = Await bucket 
Dim result As String = Await t 

Я могу сделать ошибку уйти, добавив Async к охватывающему Суб декларации. Однако, если я это сделаю, я получаю еще одну ошибку, потому что метод содержания - main(), и это консольное приложение.

Ошибка 1 Метод «Основной» не может быть помечен как «Асинхронный».

Так что я думаю, мой вопрос в том, как я могу использовать Await асинхронную задачу без создания метода Async? Мой код ниже - это просто тестер для реализации в проекте WinForms, и я предпочел бы держаться подальше от не-родных .NET-штук.

Ниже приведен полный код, который я преобразован из C# вместе с моей немного кода, который делает вычисления из SHA1 хэшей для файлов:

Option Strict On 
Option Explicit On 

Imports System.IO 
Imports System.Threading 
Imports System.Threading.Tasks 


Module Module1 

    Async Sub main() 
     ' From the MSDN article 
     Dim taskArr As Task(Of Integer)() = {Task(Of Integer).Delay(3000).ContinueWith(Function(x) 3I), _ 
              Task(Of Integer).Delay(1000).ContinueWith(Function(x) 1I), _ 
              Task(Of Integer).Delay(2000).ContinueWith(Function(x) 2I), _ 
              Task(Of Integer).Delay(5000).ContinueWith(Function(x) 5I), _ 
              Task(Of Integer).Delay(4000).ContinueWith(Function(x) 4I)} 

     For Each bucket As Task(Of Task(Of Integer)) In Interleaved(taskArr) 
      Dim t As Task(Of Integer) = Await bucket ' Error Here 
      Dim result As Integer = Await t ' Error Here 
      Console.WriteLine("{0}: {1}", DateTime.Now, result) 
     Next 

     'My bit of code for computing the file hashes 
     Dim tasks As New List(Of Task(Of String)) 
     Array.ForEach(New DirectoryInfo("C:\StackOverflow").GetFiles("*", SearchOption.AllDirectories), Sub(x) tasks.Add(getHashAsync(x))) 

     For Each bucket As Task(Of Task(Of String)) In Interleaved(tasks) 
      Dim t As Task(Of String) = Await bucket ' Error Here 
      Dim result As String = Await t ' Error Here 
      Console.WriteLine(result) 
     Next 
    End Sub 

    ' Original C# code that I converted to VB myself 
    Public Function Interleaved(Of T)(tasks As IEnumerable(Of Task(Of T))) As Task(Of Task(Of T))() 
     Dim inputTasks As List(Of Task(Of T)) = tasks.ToList() 
     Dim buckets() As TaskCompletionSource(Of Task(Of T)) = New TaskCompletionSource(Of Task(Of T))(inputTasks.Count - 1I) {} 
     Dim results() As Task(Of Task(Of T)) = New Task(Of Task(Of T))(buckets.Length - 1I) {} 

     For i As Integer = 0I To buckets.Count - 1I Step 1I 
      buckets(i) = New TaskCompletionSource(Of Task(Of T))() 
      results(i) = buckets(i).Task 
     Next 

     Dim continuation As New Action(Of Task(Of T))(Function(completed As Task(Of T)) 
                  Dim bucket As TaskCompletionSource(Of Task(Of T)) = buckets(Interlocked.Increment(-1I)) 
                  Return bucket.TrySetResult(completed) 
                 End Function) 

     For Each inputTask As Task(Of T) In inputTasks 
      inputTask.ContinueWith(continuation, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) 
     Next 

     Return results 
    End Function 

    ' Get the sha1 hash of the file 
    Private Async Function getHashAsync(fle As FileInfo) As Task(Of String) 
     Using strm As New IO.FileStream(fle.FullName, FileMode.Open, FileAccess.Read, FileShare.Read) 
      Return Await New Task(Of String)(Function() As String 
               Dim sb As New Text.StringBuilder() 
               Using sha1 As New System.Security.Cryptography.SHA1CryptoServiceProvider() 
                Array.ForEach(sha1.ComputeHash(strm), Sub(x As Byte) sb.Append(x.ToString("x2"))) 
               End Using 
               Return sb.Append(" | ").Append(fle.FullName).ToString 
              End Function) 
     End Using 

    End Function 

End Module 
+0

Я не уверен, что этот конкретный случай является хорошим кандидатом на ожидание/асинхронный процесс, поскольку это все в основном работа с ЦП, которая должна быть отключена. Достаточно использовать только «Задачи» и доступные возможности ожидания и результаты. @VMAtm предоставил хороший ответ для этого ниже. Когда вы входите в создание приложения WinForms, вы сможете украсить свои события или контроллеры Async, чтобы асинхронный/ожидающий способ мог распространяться на выполнение вашего приложения, если это необходимо. – davidallyoung

ответ

2

await является только один вариант ждать результата Task «s, второй является использование Task статические методы для sinchroniously ждут результатов всех задачи.
Насколько я могу судить, вы создаете список Tasks из Tasks из T, что-то вроде этого:

var list = new List<Task<Task<string>>>(); 

Если да, то самое простое, что вы можете сделать, это придавить список задач, а также после того, как что ждать всех задач в ней с Task.WaitAll() метод, называемый в два раза, как это:

var list = new List<Task<Task<string>>>(); 

Task.WaitAll(list.ToArray()); 
// now we aggregate the results 
var gatheredTasks = list.Select(t => t.Result); 
Task.WaitAll(gatheredTasks.ToArray()); 
foreach (var task in gatheredTasks) 
{ 
    Console.WriteLine(task.Result); 
} 

в этом случае вы получите все бонусы от TPL, так как есть много методов, используемых, чтобы сбалансировать работу и и так далее, и вы получите вам нужны результаты.

Если вы хотите получить результаты в порядке их появления, вы можете написать цикл с помощью метода Task.WaitAny(), но, на мой взгляд, это не очень хороший выбор.

2

Я не знаком с VB. NET как C#, но я думаю, что здесь можно ответить async console programs. По сути, вы хотите обернуть свою задачу async в AsyncContext и запустить ее с помощью бегуна задач. См. here еще один хороший пример.

EDIT: Если вы хотите остаться в родном .NET, вы можете сделать свой собственный бегун задачи. Уходя простой пример, я думаю, это будет выглядеть примерно так:

static void Main(string[] args) 
{ 
    Task t = AsyncMain(args); 
    t.Wait(); 
} 

async static Task AsyncMain(string[] args) 
{ 
    await Task.Delay(1000); // Real async code here. 
    Console.WriteLine("Awaited a delay of 1s"); 
    return; 
} 
+0

Похоже, что «AsyncContext» не является родным для платформы .NET, поскольку он является членом пакета Nito AsyncEx NuGet. Мой выше код - всего лишь тестер для реализации в проекте WinForms, и я предпочел бы держаться подальше от не-родных .NET-штук. –

+0

Отмечено; см. правки. –

Смежные вопросы