Pull to refresh

Comments 34

это, по-моему, не первая статья на тему await.
а вот кому вопрос? (с) пластилиновый мужик

нигде не раскрывается тема асинхронных операций самих по себе — их смысл не столько в том, чтобы ждать ответа, сколько в том, чтобы заняться другими делами.
1) отсюда мой первый вопрос — какие проблемы предполагается решить await-ом, если поведение по умолчанию аналогично синхронным вызовам? я имею ввиду кроме аргумента «меньше писать кода».
2) далее второй вопрос — чем ограничено количество одновременно запущенных await-ов?
1) Поведение по умолчанию не аналогично синхронному случаю, так как await'ом решается проблема комбинации асинхронных вызовов в асинхронный, а данная задача в синхронном случае решается плохо. Я пытался это объяснить в этой ветке (http://habrahabr.ru/blogs/webdev/108241/#comment_3425399)

2) В своей статье (http://habrahabr.ru/blogs/net/107583/) я уже писал, что код продолжения (после await, но все асинхронных вызовах) выполняется через SynchronizationContext, следовательно, наследуются ограничения конкретной реализации SynchronizationContext.
Согласен с Вами. Мне тоже кажется, что приведенные примеры (во всяком случае первый) не раскрывают смысла async/await.
ИМХО, идея состоит в том чтобы имея одну задачу, разделить ее на несколько более мелких, последовательное выполнение которых решает первоначальную задачу. При этом мы выполняем эту задачу параллельно.
То есть, например есть задача сохранить файл из интернета на диск. Она бьется на две — получить файл из интернета в память, например, и сохранить из памяти на диск. То есть, имеем метод DownloadToFile, внутри которого еще 2 — DownloadToStream, SaveToFile.
DonwloadToFile помечается async, что говорит, что его надо выполнить асинхронно. Вызов DownloadToStream предваряется await, что говорит, что в этом месте надо вызвать это метод асинхронно, вернуть управление выше, а после завершения вызванного метода продолжить выполнение с этого же места. И все это для того чтобы параллельно делать что-то еще.

И когда я понял зачем async/await, первой мыслью было «А чем это отличается от того, чтобы все это делать в параллельном Thread, например, (ну или через Task'и)?». Так вот — ничем. Это просто синтаксический сахар. Как и yield — вместо него тоже можно писать свои реализации IEnumerable и IEnumerator. Да это будет удобно, это в итоге убережет от ошибок и «велосипедов», это будет здорово и проще, но в этом нет ничего нового.

Или я чего-то не заметил?

ЗЫ. И кстати возвращать можно не только void, Task и Task<T> =)
Может быть в первом посте из этой серии будет более понятна идея, поскольку здесь всего-лишь асинхронится то, что рассказывалось там, только третьим способом.
Но, в любом случае пример (который в этой статье и приведен) показывает следующее: нам выгоднее запустить 3 асинхронные операции получения веб-респонса *одновременно*, а не последовательно, поскольку в этом случае результат мы получим тупо быстрее (причем почти в трое), поскольку большую часть времени выполнения этой операции мы просто ждем ответа от сети. И это далеко не одно и тоже, что мы их запустим в другом потоке с помощью пула потоков, поскольку в этом случае выгоды мы вообще не получим. По сути, именно это мы видим в самом «наивном» использовании новой асинхронности: мы по-сути выгоды не получаем, мы просто выполняем все операции асинхронно, возможно даже не используя новые потоки (если запустить эту функцию в обработчике клика мыши, то отзывчивость приложения будет в три раза выше). Выгоду мы получаем только в самом последнем примере, когда все три запроса делаются *параллельно*, что и показано в последнем примере.
>И это далеко не одно и тоже, что мы их запустим в другом потоке с помощью пула потоков, поскольку в этом случае выгоды мы вообще не получим

А можно вот это пояснить. Почему не получим?
Т.е. мы хотим выяснитиь в чем разница между простым вызовом синхронного метода, т.е. SyncVersion() и вызовом его через Thread Pool (например): ThreadPool.QueueUserWorkItem(o=>SyncVersion());?
Здесь нет разницы, поскольку мы как выполняли этот метод 10 секунд, так и выполняем, мы просто отвязали поток выполнения, от пока, который его инициировал. Я уже писал в комментариях, что главная выгода от асинхронных операций состоит не в этом. Главная выгода состоит в том, что мы можем повысить не только *отзывчивость* пользовательского интерфейса (что более или просто), а повысить эффективность, за счет того, что мы начнем несколько асинхронных операций *одновременно*. В этом случае мы уменьшим время выполнения, например, с 15, до 5 секунд.

Там же по ссылке вроде бы это описано, да и в комментах об этом уже написано;)
Боюсь показаться занудой, но мне кажется, что вся соль нововведений немного в другом. Запустить параллельно и асинхронно несколько асинхронных операций с обработкой результатов в контексте вызывавшего потока можно было и раньше:

public static void Bar() 
{
    var context = SynchronizationContext.Current;
    new Thread(delegate()
    {
        var requestA = HttpWebRequest.Create("http://ya.ru/");
        var requestB = HttpWebRequest.Create("http://habr.ru/");
        var futuresA = requestA.BeginGetResponse(delegate { }, null);
        var futuresB = requestB.BeginGetResponse(delegate { }, null);
        var resultA = requestA.EndGetResponse(futuresA);
        var resultB = requestB.EndGetResponse(futuresB);
        context.Post(delegate
        {
            Console.WriteLine(resultA.ContentType);
            Console.WriteLine(resultB.ContentType);
        }, null);
    }).Start();
}

И нельзя сказать, что данный код настолько плох, чтобы вводить новую конструкцию. Но вот создание собственного асинхронного метода является проблемой, точнее являлось. Раньше, для этого нужно было определить пару методов BeginXXX(), EndXXX() и реализовывать интерфейс IAsyncResult. Await вводит новую технику работы с асинхронными методами, используя которую новый асинхронный метод описывается через комбинацию старых асинхронных/синхронных методов, и это описание максимально приближено к простому описанию функции.

То есть async/await в первую очередь упрощает не использование асинхронных методов, а их определение. Во многом это нововведение подобно yield return, которое упрощает создание интераторов.
Если не вдаваться в матан

Это не матан, в крайнем случае алгебра или теория категорий (вспоминаем о монадах).
алгебра — это матан, хоть и не вышка
UFO just landed and posted this here
Мы имеем штуку, которой действительно удобно пользоваться без боязни отстрелить себе ногу.

При наличии тяжелых вычислений в коде между await'ами достаточно просто: дефолтный SynchronizationContext использует ThreadPool, следовательно можно захватить все его потоки, а потом счасливо дебажить=)
Простите, но как я понял вся суть асинхронных вызовов — это проделать несколько задач в одном потоке… не могли бы немного пояснить по поводу использования ThreadPool и асинхронных операциях — действительно ли есть требование для его использования?
Где конкретно выполняется код асинхронной операции, вызываемой через await не важно, это дело самой асинхронной операции, проблема состоит в том, где обрабатывать callback (код после await) в потоке асинхронной операции или где-то еще. Дизайнеры async/await решили для этого использовать SynchronizationContext потока, который запускает асинхронную операцию. Если это gui приложение, то SynchronizationContext выполняет код в event loop'е, если это обычный поток, в котором SynchronizationContext не переопределен, то код выполняется в ThreadPool. Я об этом писал в статье Async в C# и SynchronizationContext
Да, я статью чиал. Но после нескольких наработок на node.js где по-дефолту асинхронные операции выполняются в одном потоке, я вижу смысл wait-async'ов именно для организации выполнения задач в одном потоке. Кроме того, большинство примеров того же Липперта про CPS/асинки как раз выполняются в одном потоке.

Так что лично я если и буду юзать асинки, то буду стараться инкапсулировать выполнение в один поток. Во всяком случае в рамках одной логической задачи. Тогда и задумываться про стандартный ThreadPool не надо )

Может я и ошибаюсь, но я как-то ставлю почти как антонимы TPL и async/await в ракурсе исполнения задач — одно раскидывает по потокам, другое наоборот, позволяет ограничится одним потоком. А дальше — смотрим что нам выгодней и используем. Имхо главное — не смешивать их, иначе получим бяку :)

Я так понял, все что идет после await оно заворачивает в callback, который идет в новом потоке.
Лучше бы оно callback- и ставило в очередь и все делало в одном потоке.
Ну или чтобы был выбор.
Тк так и происходит, callback выполняется через SynchronizationContext.
Точнее, it depends. Если вы запустите подобную операцию в обработке кнопки мыши, то никаких дополнительных потоков не будет, а если в консоли — то будут (см. вывод первого примера).
Ага все почитал про SynchronizationContext.
Выбор есть.
почему бы первый код не изменить на:

static void SyncVersion()
{
  Stopwatch sw = Stopwatch.StartNew();
  string[] urls = new string[]{"http://rsdn.ru", "http://gotdotnet.ru", "http://blogs.msdn.com"}
  foreach (string url in urls)
  {
    DoDownload(url, ref sw)
  }
}

void DoDownload(string url, ref Stopwatch sw)
{
  var req = WebRequest.Create(url1);
  var res = req.GetResponse();

  Console.WriteLine("{0} : {1}, elapsed {2}ms", url, req.ContentLength, sw.ElapsedMilliseconds);
}


* This source code was highlighted with Source Code Highlighter.


поудобнее помоему и покрасивей.
Да уж, «навороченный» Linq и var-ы вынесли мне мозг… Неужели через некоторое время все будут так писать? Вторая версия С# была такой простой, такой понятной. В третьей все стало запутываться, в четвертой запуталось окончательно, пятая будет похоже еще хлеще. Видимо, разработчикам языка не давала покоя мысль, что код всем понятен с первого взгляда — и они оторвались по полной на загадочных

var tasks = (from url in urls
let webRequest = WebRequest.Create(url)
select new {Url = url, Response = webRequest.GetResponseAsync()})
.ToList();

Здесь от классического шарпа разве что ".ToList()".
Не в языке проблема. Я очень рад LINQ многие вещи он позволяет писать быстрее и проще. Это просто автор статьи злоупотребил им. Злоупотребить же можно любой конструкцией.

Вот в «Рекомендации NASA по написанию безопасных программ.»
Во многом, Си – это ассемблер высокого уровня. Это даёт значительную гибкость и открывает ящик Пандоры с возможными ошибками.

Ограничение языка Си до определенного набора разрешенных конструкций не представляется возможным, так как получившийся язык не обладал бы нужной функциональностью.

Практически все конструкции языка придуманы, чтобы программист, в конце концов, прострелил себе ногу.

Вот блин, я никогда себя фанатом LINQ-а не считал:) А где я тут им злоупотребил? Он может быть непривычен, это да, но читать (если привыкнуть) станет легче. А тут я создаю анонимный тип, который связывает URL и ответ, чтобы когда я буду итерировать ответ не нужно было по индексу обращаться в другую коллекцию, а все было сразу под рукой.
Он же читается практически так же как пишется:
взять url из urls
создать WebRequest
сделать анонимный класс с парой полей: URL и web response-ом на этот url
дернуть ToList, поскольку без него ленивая природа LINQ-а сделает свое грязное дело в результате чего асинхронные запросы будут выполняться по одному, что прибьет основную от них выгоду.

Вроде бы, опять же, не матан:)
Но согласитесь, код приведенный zabr короче, проще, понятнее. LINQ часто упрощает код, но часто усложняет. Тоже с анонимными типами var.

Как аналог сложности, можно привести VB. Там вообще весь код читается как предложения, текстом. Но чище он от этого не становится.
Но ведь мы не об этом коде говорим;) Приведенный код — это упрощение синхронной версии, а не асинхронной. Подобный код для асинхронной версии только усложнит код.

Кроме того, вы задумывались зачем вообще ввели все эти анонимные делегаты и лямбды? На самом деле, далеко не всегда следует выделять некоторую логику в отдельный метод. Например, в некоторых случаях некоторая логика неотъемлемо связана с другой логикой; она не самостоятельно. Тогда в этом случае выделение отдельного метода лишь уменьшает связанность (cohesion), и мы получаем, что одна, по сути, логика начинает «расползаться» по нескольким методам. Тогда, вместо того, чтобы взять и прочитать все в одном месте, нам нажно шариться по трем десяткам функциям, каждая из которых сама по себе ни имеет ни малейшей ценности. Именно в этих случаях повышение декларативности (с помощью того же линка) начинает играть положительную роль.

З.Ы. Анонимные типы с var-ом не имеет ничего общего (да, анонимные типы без var-ов никуда, но это не единственный кейс применения этого ключевого слова). Но фраза «анонимный тип var» — это не корректная фраза.
да, писал про «первый код». ради интереса превращу его в асинхронный )

раньше асинхронный код делал через 2 простых класса и статический массив/список откуда брались задания и асинхронно выполнялись )

посмотримс новый блекджек )
Довольно интересно, даже немного загадочно, и местами оч удобно,
но для себя решил пока остаться на Rx (PFX).

Отсрелить ногу возможность есть в 2ух случаях.

1) в методах типа async нельзя передавать параметры аля ref/out, соответсвенно будут пихать что пихнется )
2) лучше не надо обьединять PFX и async, немножко может взорваться мозг.

Но в любом случае это легче чем использовать асинкстейт, асинкрезалт.

З.Ы.

Класс операций закачки я разделил для удобства по файлам, чтобы не запутаться.
Код может содержать пару одинаковых по сути методов для наглядности.
Архив исходников: — ifolder.ru/20635091

Код:

Файл Program.cs:

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Downloader
{
  class Program
  {
    private static readonly List<Utils.UrlExt> Urls =
      new List<Utils.UrlExt>
        {
          new Utils.UrlExt(0, "http://ya.ru"),
          new Utils.UrlExt(1, "http://google.ru"),
          new Utils.UrlExt(2, "http://habrahabr.ru")
        };

    private static void Main()
    {
      Stopwatch sw = new Stopwatch();
      sw.Start();

      Console.WriteLine("Press any key to start .... ");
      Console.ReadKey();
      Console.WriteLine(Environment.NewLine);

      // ================== sync version ===================
      sw.Restart();
      Console.WriteLine("Starting Sync Version");
      Console.WriteLine();

      InnerDownloader.SyncDownload(Urls, sw);

      Console.WriteLine("End Sync Version");
      Console.WriteLine(Environment.NewLine);
      // ================ end sync version =================

      // ================== async rx version ===================
      sw.Restart();
      Console.WriteLine("Starting ASync RX (PFX) Version (.Net 3.5-4.0)");
      Console.WriteLine();

      InnerDownloader.RxASyncDownload(Urls, sw);

      Console.WriteLine("End ASync RX (PFX) Version (.Net 3.5-4.0)");
      Console.WriteLine(Environment.NewLine);
      // ================ end async rx version =================

      // ================== new async version ===================
      sw.Restart();
      Console.WriteLine("Starting New ASync Version (.Net 5.0)");
      Console.WriteLine();

      InnerDownloader.NewASyncDownload(Urls, sw);

      Console.WriteLine("End New ASync Version (.Net 5.0)");
      Console.WriteLine(Environment.NewLine);
      // ================ end old async version =================

      // ================== new async version v2 ===================
      sw.Restart();
      Console.WriteLine("Starting New ASync Version (.Net 5.0) v2");
      Console.WriteLine();

      InnerDownloader.NewASyncDownloadV2(Urls, sw);

      Console.WriteLine("End New ASync Version (.Net 5.0) v2");
      Console.WriteLine(Environment.NewLine);
      // ================ end old async version v2 =================

      // ================== new async version v3 ===================
      sw.Restart();
      Console.WriteLine("Starting New ASync Version (.Net 5.0) v3");
      Console.WriteLine();

      InnerDownloader.NewASyncDownloadV3(Urls, sw);

      Console.WriteLine("End New ASync Version (.Net 5.0) v3");
      Console.WriteLine(Environment.NewLine);
      // ================ end old async version v3 =================

      // ================== new async version v4 ===================
      sw.Restart();
      Console.WriteLine("Starting ASync RX (PFX) Version (.Net 3.5-4.0) + New ASync Version (.Net 5.0) v4");
      Console.WriteLine();

      InnerDownloader.NewASyncDownloadV4(Urls, sw);

      Console.WriteLine("End ASync RX (PFX) Version (.Net 3.5-4.0) + New ASync Version (.Net 5.0) v4");
      Console.WriteLine(Environment.NewLine);
      // ================ end old async version v4 =================

      Console.WriteLine("Press any key to exit .... ");
      Console.ReadKey();
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Файл Utils.cs
using System;
using System.IO;
using System.Net;

namespace Downloader
{
  public class Utils
  {
    public static int GetLenght(WebResponse response)
    {
      string result = "";
      var stream = response.GetResponseStream();

      if (stream != null)
      {
        using (var sr = new StreamReader(stream))
        {
          result = sr.ReadToEnd();
        }
      }

      return result.Length;
    }

    public class UrlExt
    {
      public int Index;
      public string Url;

      internal UrlExt(int index, string url)
      {
        Index = index;
        Url = url;
      }
    }

    public static int DrawTextProgressBar(int progress, int total, string data)
    {
      progress = progress + 1;

      //draw empty progress bar
      Console.CursorLeft = 0;
      Console.Write("["); //start
      Console.CursorLeft = 12;
      Console.Write("]"); //end
      Console.CursorLeft = 1;
      const float onechunk = 3.5F;

      //draw filled part
      int position = 1;
      for (int i = 0; i < onechunk * progress; i++)
      {
        Console.BackgroundColor = ConsoleColor.Gray;
        Console.CursorLeft = position++;
        Console.Write(" ");
      }

      //draw unfilled part
      for (int i = position; i <= 11; i++)
      {
        Console.BackgroundColor = ConsoleColor.Black;
        Console.CursorLeft = position++;
        Console.Write(" ");
      }

      //draw totals
      Console.CursorLeft = 14;
      Console.BackgroundColor = ConsoleColor.Black;
      Console.Write("{0} of {1}, {2} ", progress, total, data); //blanks at the end remove any excess

      Console.WriteLine(Environment.NewLine);

      return 0;
    }
  }
}


* This source code was highlighted with Source Code Highlighter.


Файл InnerDownloaderSync.cs

using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading;

namespace Downloader
{
  /// <summary>
  /// Синхронная версия
  /// </summary>
  public partial class InnerDownloader
  {
    public static void SyncDownload(List<Utils.UrlExt> urls, Stopwatch sw)
    {
      for (int index = 0; index < urls.Count; index++)
      {
        string result = DoSyncDownload(urls[index].Url, sw);
        Utils.DrawTextProgressBar(index, urls.Count, result);
      }
    }

    static string DoSyncDownload(string url, Stopwatch sw)
    {
      var req = (HttpWebRequest)WebRequest.Create(url);
      req.AllowAutoRedirect = true;

      var res = (HttpWebResponse)req.GetResponse();

      return string.Format("{0}: {1}, {2}ms, Thread:{3}", url, Utils.GetLenght(res),
                 sw.ElapsedMilliseconds, Thread.CurrentThread.ManagedThreadId);

    }
  }
}


* This source code was highlighted with Source Code Highlighter.


Файл InnerDownloaderRx.cs

using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Downloader
{
  /// <summary>
  /// Версия для работы через Reactive, PFX
  /// </summary>
  public partial class InnerDownloader
  {
    public static void RxASyncDownload(List<Utils.UrlExt> urls, Stopwatch sw)
    {
      Parallel.For(0, urls.Count,
             index =>
             {
               string result = DoSyncDownload(urls[index].Url, sw);
               Utils.DrawTextProgressBar(index, urls.Count, result);
             }
        );
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Файл InnerDownloaderASyncStyleOne.cs

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace Downloader
{
  /// <summary>
  /// Асинхронная версия стиль № 1
  /// </summary>
  public partial class InnerDownloader
  {
    public static async void NewASyncDownload(IEnumerable<Utils.UrlExt> urls, Stopwatch sw)
    {
      int count = urls.Count();
      var tasks =
        (
          from url in urls
          let result = DoASyncDownloadV1(url.Url)
          select new
          {
            Response = result,
            Output = Utils.DrawTextProgressBar
            (
              url.Index, count,
              string.Format("{0}: {1}, {2}ms, Thread:{3}",
                url.Url, Utils.GetLenght(result.Result),
                sw.ElapsedMilliseconds, Thread.CurrentThread.ManagedThreadId)
            )
          }
        ).ToList();

      TaskEx.WhenAll(tasks.Select(t => t.Response));
    }

    static Task<WebResponse> DoASyncDownloadV1(string url)
    {
      var req = (HttpWebRequest)WebRequest.Create(url);
      req.AllowAutoRedirect = true;

      return req.GetResponseAsync();
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Файл InnerDownloaderASyncStyleTwo.cs

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace Downloader
{
  /// <summary>
  /// Асинхронная версия стиль № 2
  /// </summary>
  public partial class InnerDownloader
  {
    public static void NewASyncDownloadV2(List<Utils.UrlExt> urls, Stopwatch sw)
    {
      var task = InnerNewASyncDownloadV2(urls, sw);
      task.Wait();
    }

    private static async Task InnerNewASyncDownloadV2(List<Utils.UrlExt> urls, Stopwatch sw)
    {
      int count = urls.Count();
      for (int index = 0; index < count; index++)
      {
        var result = await DoASyncDownloadV2(urls[index].Url);
        var data = string.Format("{0}: {1}, {2}ms, Thread:{3}",
                     urls[index].Url,
                     Utils.GetLenght(result),
                     sw.ElapsedMilliseconds,
                     Thread.CurrentThread.ManagedThreadId);

        Utils.DrawTextProgressBar(index, urls.Count, data);
      }
    }

    static Task<WebResponse> DoASyncDownloadV2(string url)
    {
      var req = (HttpWebRequest)WebRequest.Create(url);
      req.AllowAutoRedirect = true;

      return req.GetResponseAsync();
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Файл InnerDownloaderASyncStyleThree.cs

using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace Downloader
{
  /// <summary>
  /// Асинхронная версия стиль № 3
  /// </summary>
  public partial class InnerDownloader
  {
    public static void NewASyncDownloadV3(List<Utils.UrlExt> urls, Stopwatch sw)
    {
      for (int index = 0; index < urls.Count; index++)
      {
        string result = DoASyncDownloadV3(urls[index].Url, sw);
        Utils.DrawTextProgressBar(index, urls.Count, result);
      }
    }

    private static string DoASyncDownloadV3(string url, Stopwatch sw)
    {
      List<string> output = new List<string> { "" };
      var response = InnerNewASyncDownloadV3(url, sw, output);
      response.Wait();

      return output[0];
    }

    private static async Task InnerNewASyncDownloadV3(string url, Stopwatch sw, IList<string> output)
    {
      var result = await DoASyncDownloadV3(url);
      output[0] = string.Format("{0}: {1}, {2}ms, Thread:{3}",
                  url,
                  Utils.GetLenght(result),
                  sw.ElapsedMilliseconds,
                  Thread.CurrentThread.ManagedThreadId);
    }

    static Task<WebResponse> DoASyncDownloadV3(string url)
    {
      var req = (HttpWebRequest)WebRequest.Create(url);
      req.AllowAutoRedirect = true;

      return req.GetResponseAsync();
    }
  }
}


* This source code was highlighted with Source Code Highlighter.


Файл InnerDownloaderRxASync.cs

using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Downloader
{
  /// <summary>
  /// Асинхронная версия 3 + PFX
  /// </summary>
  public partial class InnerDownloader
  {
    public static void NewASyncDownloadV4(List<Utils.UrlExt> urls, Stopwatch sw)
    {
      Parallel.For(0, urls.Count,
             index =>
             {
               string result = DoASyncDownloadV3(urls[index].Url, sw);
               Utils.DrawTextProgressBar(index, urls.Count, result);
             }
        );
    }
  }
}

* This source code was highlighted with Source Code Highlighter.
Букав просто дофига! Не факт, что все понял и осилил, но пару замечаний есть.

1. Я вообще не въехал, что за UrlExt, поскольку я нигде не увидел использование поля Index этого класса.
2. RxASyncDownload никакого отношения к RX не имеет. Там есть PFX, но нет Rx-a (вот тут я писал про rx, глянь последний пример плз). Здесь мы стартанем такое количество задач параллельно, сколько у нас ядер (кажись именно такое поведение по умолчанию в PFX) и здесь, кстати, оно ни к чему, поскольку Parallel.For призвано прежде всего для распараллеливания CPU-bound операций, а у нас здесь IO-bound, так что нам здесь нет смысла ограничиваться количеством параллельных операций, равному количеству ядер процессора (ну, логическому количество процессоров).
3. ИМХО, нет смысла выделять отдельный метод DoASyncDownloadV1, поскольку он не самостоятелен и все время приходится смотреть, что же он такое делает.
4. Не увидел разницы между NewASyncDownload и NewASyncDownloadV2. В первом случае перебор всех урлов производится с помощью LINQ, а во втором случае — с помощью простого цикла. При этом для NewASyncDownloadV2 введено целых два вспомогательных метода, которые нужно анализировать, чтобы понять, что там происходит. Опять ИМХО, это личшее.
5. NewASyncDownloadV3 мне практически вывихнул мозг, поскольку там аж четыре метода. Зачем-то используется List output, который передается другому методу, который изменяет значение по нулевому индексу, и потом это же значение (из нулевого индекса) возвращается из метода DoASyncDownloadV3. И, как я понял, все это для того, чтобы выполнить все эти операции асинхронно, но начинать каждую следующую по завершению предыдущей асинхронной операции.
По сути, это то, что привел в самом первом примере, когда в синхронную версию добавил модификаторов async и await.
6. NewASyncDownloadV4 и я опять не понял в чем смысл? Зачем использовать PFX, когда мы можем просто начать последовательно несколько асинхронных операций и потом уже ждать их завершения? Я например, не знаю, как будет параллелиться этот код:) Вы знаете, сколько операций будет запущено асинхронно одновременно на моем (именно моем) компьютере? Опять повторюсь, Parallel.For — это для параллельных вычислений, а не для параллельного ввода-вывода.

З.Ы. Написано очень много букв и многие из них вполне разумны. Единственное, на что я хочу обратить внимание, так это на то, что разбивать все на методы, которые не самостоятельны не всегда хорошо. Проще, чтобы было сразу же в одном методе видно, что есть что.
А так, молодца!!!
1. CNTRL+F рулит :))

public class UrlExt
{
public int Index;
public string Url;

internal UrlExt(int index, string url)
{
Index = index;
Url = url;
}
}

хотя можно и сделать структурой.

2. Я имел ввиду PFX, просто теперь оно входит в Rx, в этом контексте… надо было указать мне )
3. Это задел на будущее для доп обработок, например ошибок или настройки реквеста.
4. визуальное не больше того.
5. да именно так.
6. я посмотрел можно ли это сделать, но выше заметил что нехояшо это.

З.Ы. Визуально мне очень понравился асинхронный вариант №1 у Вас он последний в топике.

Капитан, благодарю! Но я правда только сейчас нашел использование поля Index. Оно есть только NewASyncDownload, хотя этот класс используется во всех методах.
2. Rx — ставится отдельно, а PFX входит в состав .net 4.0
3. Понял, но я бы выделил его тогда, когда появится в нем необходимость.
4. Не понял:)
5. Ок
6. Ок

Блин, почему здесь нет смайлов, аналогичных rsdn-овским, вот с таким общением, явно нужен смайл :bear:!
2. привычка хех, забывыю периодечиски что входит, поэтому все равно ставлю )
3. да Вы правы, но и в заделе на будущее есть резон.
4. это по сути одно и то же, я просто показываю сразу, что можно так можно так, ничего не имею против LINQ )
Потому что такие операции читабельнее были бы в виде method groups мне кажется.
Но это на вкус и цвет.

Извините что не написал ИМХО :)
Вся выгода от *одновременного* выполнения нескольких асинхронных операций. Этим кодом я пытался показать, что у нас три асинхронные операции, а то, что они однотипные — это не важно, ну, просто так получилось.
стиль изложения потрясен :) я не программист (для себя не считается ;) ), но даже я читал взахлеб.
Sign up to leave a comment.

Articles