2010-12-12 2 views
4

Я пытаюсь создать коллекцию веб-запросов FTP для загрузки коллекции файлов.ThreadPool с несколькими потоками, создающими временные запросы FTP

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

Вот код:

internal static void DownloadLogFiles(IEnumerable<string> ftpFileNames, string localLogsFolder) 
{ 
    BotFinder.DeleteAllFilesFromDirectory(localLogsFolder); 

    var ftpWebRequests = new Collection<FtpWebRequest>(); 

    // Create web request for each log filename 
    foreach (var ftpWebRequest in ftpFileNames.Select(filename => (FtpWebRequest) WebRequest.Create(filename))) 
    { 
     ftpWebRequest.Credentials = new NetworkCredential(BotFinderSettings.FtpUserId, BotFinderSettings.FtpPassword); 
     ftpWebRequest.KeepAlive = false; 
     ftpWebRequest.UseBinary = true; 
     ftpWebRequest.CachePolicy = NoCachePolicy; 
     ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile; 
     ftpWebRequests.Add(ftpWebRequest); 
    } 

    var threadDoneEvents = new ManualResetEvent[ftpWebRequests.Count]; 

    for (var x = 0; x < ftpWebRequests.Count; x++) 
    { 
     var ftpWebRequest = ftpWebRequests[x]; 
     threadDoneEvents[x] = new ManualResetEvent(false); 
     var threadedFtpDownloader = new ThreadedFtpDownloader(ftpWebRequest, threadDoneEvents[x]); 
     ThreadPool.QueueUserWorkItem(threadedFtpDownloader.PerformFtpRequest, localLogsFolder);    
    } 

    WaitHandle.WaitAll(threadDoneEvents); 
} 

class ThreadedFtpDownloader 
{ 
    private ManualResetEvent threadDoneEvent; 
    private readonly FtpWebRequest ftpWebRequest; 

    /// <summary> 
    /// 
    /// </summary> 
    public ThreadedFtpDownloader(FtpWebRequest ftpWebRequest, ManualResetEvent threadDoneEvent) 
    { 
     this.threadDoneEvent = threadDoneEvent; 
     this.ftpWebRequest = ftpWebRequest; 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <param name="localLogsFolder"> 
    /// 
    /// </param> 
    internal void PerformFtpRequest(object localLogsFolder) 
    { 
     try 
     { 
      // TIMEOUT IS HAPPENING ON LINE BELOW 
      using (var response = ftpWebRequest.GetResponse()) 
      { 
       using (var responseStream = response.GetResponseStream()) 
       { 
        const int length = 1024*10; 
        var buffer = new Byte[length]; 
        var bytesRead = responseStream.Read(buffer, 0, length); 

        var logFileToCreate = string.Format("{0}{1}{2}", localLogsFolder, 
             ftpWebRequest.RequestUri.Segments[3].Replace("/", "-"), 
             ftpWebRequest.RequestUri.Segments[4]); 

        using (var writeStream = new FileStream(logFileToCreate, FileMode.OpenOrCreate)) 
        { 
         while (bytesRead > 0) 
         { 
          writeStream.Write(buffer, 0, bytesRead); 
          bytesRead = responseStream.Read(buffer, 0, length); 
         } 
        } 
       } 
      } 

      threadDoneEvent.Set(); 
     } 
     catch (Exception exception) 
     { 
      BotFinder.HandleExceptionAndExit(exception); 
     } 
    } 
} 

Это, кажется, загрузив первые два файла (с использованием двух потоков Я предполагаю), но то тайм-аут, похоже, возникает, когда эти полные и приложения пытаются перейти к следующему файлу.

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


Собирался оставить комментарий, но, вероятно, легче читать в ответ:

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

отладки кода, я могу видеть, что, когда он получает:

ThreadPool.QueueUserWorkItem(threadedFtpDownloader.PerformFtpRequest, localLogsFolder); 

Он входит в метод PerformFtpRequest для веб-запроса каждого FTP и вызывает ftpWebRequest.GetResponse(), но затем только продвигается вперед для первых двух запросов. Остальные запросы остаются активными, но не идут дальше, пока первые два не закончатся. Таким образом, это в основном означает, что они остаются открытыми, ожидая завершения других запросов перед запуском.

Я думаю, что решение этой проблемы либо разрешило бы выполнение всех запросов сразу (свойство ConnectionLimit здесь не действует), либо предотвратить выполнение вызова GetResponse, пока он не будет готов использовать ответ.

Любые хорошие идеи по наилучшему способу решить эту проблему? На данный момент все, что я могу представить, - это хакерские решения, которые я бы хотел избежать :)

Спасибо!

ответ

3

Вы должны получить Servicepoint для запроса и установки ConnectionLimit

ServicePoint sp = ftpRequest.ServicePoint; 
sp.ConnectionLimit = 10; 

по умолчанию ConnectionLimit 2 - именно поэтому вы видите, что поведение.

UPDATE: Смотрите этот ответ для более подробного объяснения:

How to improve the Performance of FtpWebRequest?

+0

Спасибо мат. Я попытался добавить этот код в цикл foreach, который создавал FtpWebRequests, но получал такую ​​же проблему. Это где я должен устанавливать ограничение на соединение? – timothyclifford

+0

Добавлена ​​ссылка на вопрос/ответ с более подробной информацией. ServicePoint - одна из тех вещей, о которых вы никогда не слышали, пока вы не стучите головой несколько раз. –

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

  • Нет связанных вопросов^_^