await/async и Dns GetHostEntry()
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 02.04.14 10:54
Оценка:
Непонятная разница в количестве используемых потоков для кода с await/async и для голой Dns.BeginGetHostEntry/EndGetHostEntry.

Если я вызываю в цикле для некоторого дипазона IP адресов

Dns.BeginGetHostEntry(adr, new AsyncCallback(GetHostEntryCallback), adr);


и потом ловлю результаты в GetHostEntryCallback(IAsyncResult ar), то результат примерно такой (в квдратных скобках [] — идентификаторы потоков):

15:38:03 [ 1]: Начинаем сканирование...
15:38:03 [ 3]: 192.168.4.120     pc
15:38:03 [ 4]: 192.168.4.121     ak
15:38:03 [ 3]: 192.168.4.122     va
15:38:03 [ 4]: 192.168.4.123     mo
15:38:03 [ 3]: 192.168.4.124     pi
15:38:03 [ 4]: 192.168.4.125     cr
15:38:03 [ 4]: 192.168.4.127     se
15:38:04 [ 5]: 192.168.4.129     eg
15:38:05 [ 7]: 192.168.4.132     sh
15:38:06 [ 9]: 192.168.4.135     va
15:38:12 [ 3]: 192.168.4.126     Этот хост неизвестен
15:38:12 [ 4]: 192.168.4.128     Этот хост неизвестен
15:38:13 [ 5]: 192.168.4.130     Этот хост неизвестен
15:38:13 [ 6]: 192.168.4.131     Этот хост неизвестен
15:38:14 [ 7]: 192.168.4.133     Этот хост неизвестен
15:38:15 [ 8]: 192.168.4.134     Этот хост неизвестен
15:38:15 [ 1]: Нажмите Enter для завершения...


всего 8 потоков, 12 сек, операции для отсутствующих ip, самые длительные, висят одновременно. Причем количество потоков можно увеличить еще больше (и сократить время) если задать например ThreadPool.SetMinThreads(50, 50);

Если же в цикле вызывать

IPHostEntry curhost = await Dns.GetHostEntryAsync(adr);


или так:

Task<IPHostEntry> getHostTask = Task<IPHostEntry>.Factory.FromAsync<IPAddress>(Dns.BeginGetHostEntry, Dns.EndGetHostEntry, adr, null);
IPHostEntry curhost = await getHostTask;


то результат такой:

15:32:03 [ 1]: Начинаем сканирование...
15:32:03 [ 3]: 192.168.4.120     pc
15:32:03 [ 3]: 192.168.4.121     ak
15:32:03 [ 4]: 192.168.4.122     va
15:32:03 [ 3]: 192.168.4.123     mo
15:32:03 [ 4]: 192.168.4.124     pi
15:32:03 [ 3]: 192.168.4.125     cr
15:32:12 [ 4]: 192.168.4.126     Этот хост неизвестен
15:32:12 [ 3]: 192.168.4.127     se
15:32:21 [ 4]: 192.168.4.128     Этот хост неизвестен
15:32:21 [ 3]: 192.168.4.129     eg
15:32:30 [ 3]: 192.168.4.130     Этот хост неизвестен
15:32:39 [ 4]: 192.168.4.131     Этот хост неизвестен
15:32:39 [ 3]: 192.168.4.132     sh
15:32:48 [ 5]: 192.168.4.133     Этот хост неизвестен
15:32:57 [ 4]: 192.168.4.134     Этот хост неизвестен
15:32:57 [ 3]: 192.168.4.135     va
15:32:57 [ 1]: Нажмите Enter для завершения...


4 потока, 54 сек :( И ThreadPool.SetMinThreads(50, 50) никак на результат не влияет :((

Вопрос — как порулить количеством потоков для await/async (Task)?
Re: await/async и Dns GetHostEntry()
От: Sinix  
Дата: 02.04.14 11:01
Оценка: 14 (1)
Здравствуйте, Odi$$ey, Вы писали:

OE>Непонятная разница в количестве используемых потоков для кода с await/async и для голой Dns.BeginGetHostEntry/EndGetHostEntry.

OE>Вопрос — как порулить количеством потоков для await/async (Task)?

А у вас точно все задачи запущены _до_ первого await?

Иначе получается последовательность "запускаем задачу, продолжаем после её завершения, запускаем следующую ..." и по времени выполнения код несильно будет отличаться от однопоточного.
Re[2]: await/async и Dns GetHostEntry()
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 02.04.14 11:20
Оценка:
Здравствуйте, Sinix, Вы писали:

S>А у вас точно все задачи запущены _до_ первого await?


вопрос не понял У меня так — консольное приложение, в Main вызывается:

var res = ScanIps();
res.Wait();


где (если выкинуть обработку исключений)

private static async Task ScanIps()
{
  ...
  for (... adr по диапазону IP ...)
  {
    IPHostEntry curhost = await Dns.GetHostEntryAsync(adr);

    Console.WriteLine( adr, hostname и проч );
  }

}


а как надо?
Re[3]: await/async и Dns GetHostEntry()
От: SoLame  
Дата: 02.04.14 11:31
Оценка:
Здравствуйте, Odi$$ey, Вы писали:



OE>где (если выкинуть обработку исключений)


OE>
OE>private static async Task ScanIps()
OE>{
OE>  ...
OE>  for (... adr по диапазону IP ...)
OE>  {
OE>    IPHostEntry curhost = await Dns.GetHostEntryAsync(adr);

OE>    Console.WriteLine( adr, hostname и проч );
OE>  }

OE>}
OE>


OE>а как надо?


Надо юзать Task.WhenAll.
Re[3]: await/async и Dns GetHostEntry()
От: Sinix  
Дата: 02.04.14 11:36
Оценка:
Здравствуйте, Odi$$ey, Вы писали:


OE>а как надо?


что-то типа (если в лоб)
// Запускаем задачи
var tasks = ipAdresses.Select(a=>Dns.GetHostEntryAsync(a)).ToArray();

// Дожидаемся, пока все не отработают.
await Task.WhenAll(tasks);
foreach (var task in tasks)
{
  var data = await task; // Ну, или task.Result, после завершения WhenAll() все задачи уже выполнятся.
}


Если нужно обрабатывать результаты по мере поступления, см есть пример в msdn. Если последовательность сложная, возможно будет удобнее навернуть то же самое через Rx.
Re[4]: await/async и Dns GetHostEntry()
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 02.04.14 14:52
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Если нужно обрабатывать результаты по мере поступления,


да, надо

S>см есть пример в msdn. Если последовательность сложная, возможно будет удобнее навернуть то же самое через Rx.


глянул, попробую из интереса прикрутить к своему примеру, но что-то по-моему это посложнее будет чем Dns.BeginGetHostEntry/EndGetHostEntry Непонятно всё как-то с этим async/await, в простых случаях это не надо, т.к. и так все просто, а в реальном случае всё только сложнее получается...
Re[5]: await/async и Dns GetHostEntry()
От: SoLame  
Дата: 02.04.14 16:05
Оценка: 177 (3) +1
Здравствуйте, Odi$$ey, Вы писали:

OE>да, надо


S>>см есть пример в msdn. Если последовательность сложная, возможно будет удобнее навернуть то же самое через Rx.


OE>глянул, попробую из интереса прикрутить к своему примеру, но что-то по-моему это посложнее будет чем Dns.BeginGetHostEntry/EndGetHostEntry Непонятно всё как-то с этим async/await, в простых случаях это не надо, т.к. и так все просто, а в реальном случае всё только сложнее получается...



  string[] hosts=new string[]{"ya.ru",
        "yandex.ru",
        "google.ru",
        "test1.ru",
        "test2.ru",
        "test3.ru",
        "test4.ru"
        };

   var tasks = hosts.Select<string,Task>(async host=>
                {
                    try
                    {
                        var ips = await Dns.GetHostAddressesAsync(host);
                        Console.WriteLine("{0} - {1}", host, ips[0]);
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("{0} - ошибка", host);
                    }
                }).ToArray();
 Task.WaitAll(tasks);
Re[6]: await/async и Dns GetHostEntry()
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 03.04.14 02:34
Оценка:
Здравствуйте, SoLame, Вы писали:

SL> var tasks = hosts.Select<string,Task>(async host=>


о, вот так компактно и количеством потоков можно рулить
Re: await/async и Dns GetHostEntry()
От: TK Лес кывт.рф
Дата: 03.04.14 09:09
Оценка: +1
Здравствуйте, Odi$$ey, Вы писали:

OE>Вопрос — как порулить количеством потоков для await/async (Task)?


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