【问题标题】:ThreadPool with multiple threads creating FTP requests times out多线程创建 FTP 请求的 ThreadPool 超时
【发布时间】:2011-05-24 09:35:45
【问题描述】:

我正在尝试创建 FTP Web 请求的集合以下载文件集合。

在单个线程中正常工作,但现在尝试使用多个线程,但出现超时异常。我想我错过了一些非常简单但似乎无法解决的问题

代码如下:

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);

它为每个 FTP Web 请求进入 PerformFtpRequest 方法并调用 ftpWebRequest.GetResponse() 但随后仅对前两个请求进行进一步处理。其余请求保持活动状态,但在前两个完成之前不要继续。所以这基本上意味着它们在等待其他请求在开始之前完成时保持打开状态。

我认为解决这个问题的方法是允许所有请求一次执行(ConnectionLimit 属性在这里不起作用)或阻止执行调用 GetResponse,直到它真正准备好使用响应。

关于解决这个问题的最佳方法有什么好的想法吗?目前我能想到的似乎都是我想避免的 hacky 解决方案:)

谢谢!

【问题讨论】:

    标签: c# asp.net multithreading threadpool ftpwebrequest


    【解决方案1】:

    您应该获取请求的 ServicePoint 并设置 ConnectionLimit

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

    默认 ConnectionLimit 为 2 - 这就是您看到该行为的原因。

    更新:请参阅此答案以获得更详尽的解释:

    How to improve the Performance of FtpWebRequest?

    【讨论】:

    • 谢谢伙计。我尝试在创建 FtpWebRequests 但遇到同样问题的 foreach 循环中添加该代码。那是我应该设置连接限制的地方吗?
    • 添加了指向问题/答案的链接,其中包含更多详细信息。 ServicePoint 是您在敲了几次头之前从未听说过的东西之一。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-23
    • 2010-11-16
    • 2018-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多