2011-07-01 3 views
4

Я пытаюсь добиться параллельной загрузки файлов через http в C#. Я пробовал несколько разных подходов, но никто из них, похоже, не работает правильно. Независимо от того, что я делаю, загрузки заканчиваются в очереди и не работают по-настоящему параллельно.Параллельная загрузка

Не могли бы вы дать мне некоторые указания или ссылку на статью, в которой описывается метод, который действительно работает?

+9

Какие подходы вы пробовали ? Отправьте код, пожалуйста. –

+3

что вы пробовали? вы начали два потока и выполнили веб-запрос в каждом из них или используете параллельную библиотеку .NET 4? покажите, что вы пытались сделать до сих пор. –

+0

Отсутствие деталей затрудняет ответ. Пожалуйста, разместите код и сообщите нам, будут ли все загрузки загружены из одного и того же/разных доменов. – spender

ответ

5

Я просто написал код, не проверял, ожидая какого-то наблюдения спасибо всем:

public class DownloadFile 
{ 
    public string Url { get; set; } 

    public string PathToSave { get; set; } 
} 


public class ParallelDownloading 
    { 
     private ConcurrentQueue<DownloadFile> _queueToDownlaod; 
     private IList<Task> _downloadingTasks; 
     private Timer _downloadTimer; 

     private int _parallelDownloads; 

     public ParallelDownloading(int parallelDownloads) 
     { 
      _queueToDownlaod = new ConcurrentQueue<DownloadFile>(); 
      _downloadingTasks = new List<Task>(); 
      _downloadTimer = new Timer(); 

      _parallelDownloads = parallelDownloads; 

      _downloadTimer.Elapsed += new ElapsedEventHandler(DownloadTimer_Elapsed); 
      _downloadTimer.Interval = 1000; 
      _downloadTimer.Start(); 

      ServicePointManager.DefaultConnectionLimit = parallelDownloads; 
     } 

     public void EnqueueFileToDownload(DownloadFile file) 
     { 
      _queueToDownlaod.Enqueue(file); 
     } 

     void DownloadTimer_Elapsed(object sender, ElapsedEventArgs e) 
     { 
      StartDownload(); 
     } 

     private void StartDownload() 
     { 
      lock (_downloadingTasks) 
      { 
       if (_downloadingTasks.Count < _parallelDownloads && _queueToDownlaod.Count > 0) 
       { 
        DownloadFile fileToDownload; 
        if (_queueToDownlaod.TryDequeue(out fileToDownload)) 
        { 
         var task = new Task(() => 
         { 
          var client = new WebClient(); 
          client.DownloadFile(fileToDownload.Url, fileToDownload.PathToSave); 
         }, TaskCreationOptions.LongRunning); 

         task.ContinueWith(DownloadOverCallback, TaskContinuationOptions.None); 

         _downloadingTasks.Add(task); 
         task.Start(); 
        }  
       } 
      } 
     } 

     public void DownloadOverCallback(Task downloadingTask) 
     { 
      lock (_downloadingTasks) 
      { 
       _downloadingTasks.Remove(downloadingTask); 
      } 
     } 
    } 

Вы можете проверить его с этим:

ParallelDownloading p = new ParallelDownloading(5); 

     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file1.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file2.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file3.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file4.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file5.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file6.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file7.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file8.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file9.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file10.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file11.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file12.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file13.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file14.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file15.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file16.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file17.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file18.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
     p.EnqueueFileToDownload(new DownloadFile() { PathToSave = @"c:\file19.f", Url = @"http://download.thinkbroadband.com/20MB.zip" }); 
0

Это потому, что вы работаете на одноядерном компьютере?

TPL будет использовать столько потоков, сколько у вас есть. Есть способы заставить его работать, используя больше потоков, если хотите.

0

Загрузка файла - это связанный вызов ввода-вывода, который является параллельным или не первым, что вы должны удостовериться в том, что вы создаете безпоточный асинхронный вызов для загрузки одного файла. Такие методы, как Task.Run, task.Start, основаны на потоках, и они не должны использоваться для связанных вызовов ввода/вывода, иначе вы будете запускать параллельные загрузки, но сразу же заблокируете весь ваш процессор, каждое ядро, сидящее там в режиме ожидания, ждет вызов загрузки для возврата.

Вместо этого вы должны использовать шаблон async/await и ждать вашего метода загрузки async. Это безболезненно, предполагая, что у вас есть истинный метод загрузки async, но большинство библиотек это предоставляют.

Теперь, если вы распараллеливаете этот вызов ввода-вывода, сохраните все возвращенные задачи в коллекции, а в конце вы можете использовать wait. Tasks.WhenAll (tasks); ждать всех задач.

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

я реализовал Parallel Processing API, который позволяет сделать Параллельное Threadless Асинхронный вызовы с I/O Параметры регулирования и т.д.

Вы можете иметь внешний вид и использование: https://www.nuget.org/packages/ParallelProcessor/

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