【问题标题】:Spawning a lot of threads. System seems to queue threads产生很多线程。系统似乎将线程排队
【发布时间】:2015-11-23 06:56:44
【问题描述】:

我对线程很陌生,所以我的实现无疑是非常初级的。

我正在尝试生成 8 个或更多线程,每个线程使用 FTPWebRequest 下载文件,以并行运行。

所有线程似乎都已生成并完成它们的工作,但任何时候只有两个线程在工作。他们似乎在排队。

谁能建议我可能做错了什么以及如何解决这个问题?

private void btnDownload_Click(object sender, EventArgs e)
    {
        int itemCount = lBoxList.Items.Count;
        string[] listOfFilesToDownload = new string[itemCount];

        for (int i = 0; i < itemCount; i++)
        {
            listOfFilesToDownload[i] = (string)lBoxList.Items[i];
        }

        FtpParallelDownload("ftp://ftp.somedomain.com/sub/sub2/sub3/", (int)this.nudNumberOfThreads.Value, listOfFilesToDownload, tBoxDownloadPath.Text);

    }

public static void FtpParallelDownload(string serverUri, int maxNumberOfThreads, string[] listOfFilesToDownload, string downloadPath)
    {
        int progressPercent = 0;

        // Validate number of threads requested
        if (!(maxNumberOfThreads >= 1))
        {
            ArgumentException e = new ArgumentException();
            throw e;
        }

        // Calc number of files based on array length
        int numberOfFiles = listOfFilesToDownload.Length;

        // Don't spawn more threads than files
        if (maxNumberOfThreads > numberOfFiles)
        {
            maxNumberOfThreads = numberOfFiles;
        }            

        // Thread spawning

        List<Thread> threadPool = new List<Thread>();

        int runningThreadCount = 0;



        for (int i = 0; i < numberOfFiles; i++)
        {
            if (runningThreadCount < maxNumberOfThreads)
            {
                Thread workerThread = new Thread(() => FtpDownloadFile(serverUri, listOfFilesToDownload[i], downloadPath));
                threadPool.Add(workerThread);
                workerThread.Start();
                runningThreadCount++;                   
            }
            else
            {
                i--;
            }

            List<Thread> removeThreadList = new List<Thread>();
            foreach (Thread t in threadPool)
            {
                if (!t.IsAlive)
                {
                    removeThreadList.Add(t);
                }
            }

            foreach (Thread t in removeThreadList)
            {
                threadPool.Remove(t);
                runningThreadCount--;
            }
            removeThreadList.Clear();
        }

        MessageBox.Show("DOWNLOADS COMPLETE!");
    }

    private static void FtpDownloadFile(string serverUri, string fileName, string downloadPath)
    {
        try
        {
            FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(new Uri(serverUri + fileName));
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential("anonymous", "noreply@mydomain.com");
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            FileStream writeStream = new FileStream(downloadPath + "/" + fileName, FileMode.Create);

            int Length = 4096;
            Byte[] buffer = new Byte[Length];
            int bytesRead = responseStream.Read(buffer, 0, Length);
            while (bytesRead > 0)
            {
                writeStream.Write(buffer, 0, bytesRead);
                bytesRead = responseStream.Read(buffer, 0, Length);
                Thread.Sleep(10);

            }
            writeStream.Close();
            response.Close();
            responseStream.Close();
        }

        catch (WebException wEx)
        {
            MessageBox.Show(wEx.Message, "Download Error");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Download Error");
        }
    }

threadPool 是一个Thread&lt;List&gt;,我用它来监视活动线程的数量,以便在完成工作时产生更多线程。

【问题讨论】:

  • “threadPool 是一个线程,我用它来监视活动线程的数量,以便在完成工作时产生更多线程” - 由于令人难以置信的复杂性创建线程池;调度和动态优势,通常应该避免创建自己的实现。像你这样承认自己是“线程新手”的人绝对不应该制作自己的池机制。
  • 如果没有更多细节,包括minimal reproducible example,就不可能确定发生了什么。但我会说你很有可能只是看到默认连接限制。参见例如HttpWebRequest takes a long time to send when there are a bunch at once from clientC# Manual Threading 进行可能的相关讨论。如果您在阅读完这些内容后仍然需要帮助,请改进问题。
  • “我正在尝试生成 8 个或更多线程,每个线程都通过 FTPWebRequest 下载文件”。你做错了什么是你试图使用多线程。您应该尝试使用异步 I/O。
  • 这里用线程没什么问题,8个线程没什么。这应该基本上可以工作。彼得是对的。
  • Omg,i--; 是一种创建无限循环的创意方式:) 您的整个线程管理应该替换为 PLINQ 或 Parallel.ForEach。删除所有这些。

标签: c# multithreading


【解决方案1】:

你真的应该使用现有的库而不是自己滚动。

您可以使用 Microsoft 的响应式框架 (NuGet "Rx-WinForms") 对此进行编码。

public static void FtpParallelDownload(
    string serverUri, int maxNumberOfThreads,
    string[] listOfFilesToDownload, string downloadPath)
{
    listOfFilesToDownload
        .ToObservable()
        .Select(x => Observable.Start(() => FtpDownloadFile(serverUri, x, downloadPath)))
        .Merge(maxNumberOfThreads)
        .ObserveOn(this)
        .ToArray()
        .Subscribe(xs => { }, () => { MessageBox.Show("DOWNLOADS COMPLETE!"); });
}

完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-08
    • 1970-01-01
    • 2014-01-08
    • 1970-01-01
    • 2020-12-04
    • 1970-01-01
    相关资源
    最近更新 更多