2010-12-12 40 views
4

我試圖創建一個FTP Web請求集合以下載文件集合。有多個線程創建FTP請求的ThreadPool超時

在單線程中正在正常工作,但現在正嘗試使用多線程,但正在收到超時異常。我想我失去了一些東西很簡單,但似乎無法工作了

這裏是代碼:

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 Web請求和調用ftpWebRequest.GetResponse(),但只對前兩個請求進一步前進。剩下的請求保持活動狀態,但在前兩次結束前不要再進行任何操作。所以這基本上意味着它們在開始之前等待其他請求完成時保持開放。

我認爲這個問題的解決方案要麼是允許所有的請求一次執行(ConnectionLimit屬性在這裏沒有效果),要麼阻止執行調用GetResponse直到它實際上準備好使用響應。

在解決這個問題的最佳途徑上的任何好主意?目前我所能想到的只是我想避免的哈克解決方案:)

謝謝!

回答

3

你應該得到的請求的ServicePoint並設置ConnectionLimit

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

默認ConnectionLimit爲2 - 這就是爲什麼你看到的這種行爲。

更新:看到這個答案的更透徹的解釋:

How to improve the Performance of FtpWebRequest?

+0

感謝隊友。我試圖在創建FtpWebRequests的foreach循環中添加代碼,但得到同樣的問題。那是我應該設置連接限制的地方嗎? – timothyclifford 2010-12-12 23:08:56

+0

添加了問題/答案的鏈接,提供更多詳細信息。 ServicePoint是你幾次沒有聽過的東西之一。 – 2010-12-13 00:36:59