2017-02-14 9 views
0

Я новичок в асинхронном программировании и пытаюсь написать некоторый тестовый код, чтобы посмотреть, что я могу с ним сделать.Как узнать, что все async/await завершены?

ниже - это код приложения для тестового консольного приложения, вызывающий метод async, который отправит некоторый существующий файл .docx на мой веб-api и преобразует в pdf.

static void Main(string[] args) 
    { 
     //async testing - 5 files batch 
     Console.WriteLine("Job Start:"); 

     for (int i = 0; i < 5; i++) 
     { 
      var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open); 

      //delegate 
      Action act = async() => 
      { 
       await test(filestream, i.ToString()); 
       filestream.Dispose(); 
      }; 

      act(); 
     } 

     Console.WriteLine("End."); 
     Console.ReadLine(); 
    } 

и метод:

static async Task<int> test(FileStream fs, string id) 
    { 
     var content = new StreamContent(fs); 
     var client = new HttpClient(); 

     var startTime = DateTime.Now; 

     //post to web api 
     var response = await client.PostAsync("http://localhost:50348/PDFConvert/Convert?name=" + id, content); 

     var ts = DateTime.Now - startTime; 

     Console.WriteLine("Time Cost: " + ts.TotalSeconds.ToString() + " seconds, " + response.Content.ReadAsStringAsync().Result.Replace("\"", "")); 

     client.Dispose(); 
     return 0; 

    } 

Он работает до сих пор, но одна проблема заключается в "End". напечатанный сразу после «Job Start:» в окне консоли, а затем ожидающие выходы.

Так что мой вопрос:
1. Как распечатать сообщение «Закончить» после завершения async/wait?
2. Не уверен, что это лучшая практика, поэтому любое предложение для существующего кода?

Большое спасибо.

ответ

3

вы никогда не должны делать async void, если вы не пишете обработчик события и если вы заставить его быть делегатом Action, который вызывает делегата async void.

Измените свой делегат на Func<Task>, после чего вы можете взять массив задач и сделать WaitAll на них.

static void Main(string[] args) 
{ 
    //async testing - 5 files batch 
    Console.WriteLine("Job Start:"); 

    var tasks = new Task[5]; 

    for (int i = 0; i < 5; i++) 
    { 
     var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open); 

     //Moved i.ToString() out of the delegate to fix a potential bug with variable capture. 
     var stringToPrint = i.ToString() 

     //delegate 
     Func<Task> act = async() => 
     { 
      await test(filestream, stringToPrint); 
      filestream.Dispose(); 
     }; 

     var task = act(); 
     tasks[i] = task; 
    } 

    //In a non console program you will likely want to do "await Task.WhenAll(tasks);" instead. 
    Task.WaitAll(tasks); 

    Console.WriteLine("End."); 
    Console.ReadLine(); 
} 
+0

Это работает для меня, спасибо. – blk25t

+0

@ blk25t, тогда вы должны отметить принятый ответ. –

2

Я новичок в асинхронном программировании

Я предлагаю мои async intro следует моей async best practices статьи.

Не использовать async lambdas с Action тип делегата; что приводит к методу async void. Один из недостатков методов async void заключается в том, что вы не можете легко сказать, когда они завершатся.

Асинхронный режим не является естественным с помощью консольных приложений. Вы можете заблокировать основной поток на асинхронный код, но это не очень хорошая модель для использования в любых других приложений:

static void Main(string[] args) 
{ 
    Console.WriteLine("Job Start:"); 

    MainAsync().GetAwaiter().GetResult(); 

    Console.WriteLine("End."); 
    Console.ReadLine(); 
} 

static async Task MainAsync() 
{ 
    for (int i = 0; i < 5; i++) 
    { 
    using (var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open)) 
     await test(filestream, i.ToString()); 
    } 
} 

Update из-за комментарии:

Так как вы хотите, чтобы запустить свой задачи одновременно, вы должны следовать указаниям в моем async intro post и использовать Task.WhenAll:

static async Task MainAsync() 
{ 
    var tasks = Enumerable.Range(0, 5).Select(TestFilesAsync).ToList(); 
    await Task.WhenAll(tasks); 
} 

static async Task TestFilesAsync(int i) 
{ 
    using (var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open)) 
    await test(filestream, i.ToString()); 
} 
+0

Спасибо, он работает в части «Конец», но возникает другая проблема: задачи запускаются один за другим, а не параллельно. Я понял, что консольные приложения не являются хорошим примером для тестирования, поэтому я попытаюсь написать что-то в winforms. – blk25t

+0

@ blk25t: Я обновил ответ для вашего обновленного вопроса. –

0

Вы можете использовать асинхронную лямбда внутри метода Linq Select для перебора списка задач и ждать их, чтобы завершить

var list = Enumerable.Range(1,5).Select (
      async i => { 
       var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open); 
       await test(filestream, i.ToString()); 
       filestream.Dispose(); 
       } 
    ); 
    Task.WaitAll (list.ToArray()); 

Если это не было приложение консоли, но контекст асинхронной, вы мог бы использовать WhenAll вместо WaitAll.