2010-12-12 41 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