【问题标题】:C# Multi-threading - Upload to FTP ServerC# 多线程 - 上传到 FTP 服务器
【发布时间】:2024-01-14 02:47:01
【问题描述】:

我想在我的 C# 程序中实现多线程方面寻求您的帮助。

该程序旨在将 10,000++ 个文件上传到 ftp 服务器。我计划至少实现 10 个线程以提高进程的速度。

有了这个,这是我拥有的代码行:

我已经初始化了 10 个线程:

public ThreadStart[] threadstart = new ThreadStart[10];
public Thread[] thread = new Thread[10];

我的计划是将一个文件分配给一个线程,如下:

file 1 > thread 1
file 2 > thread 2
file 3 > thread 3
.
.
.
file 10 > thread 10
file 11 > thread 1
.
.
.

所以我有以下内容:

foreach (string file in files)
{
     loop++;

     threadstart[loop] = new ThreadStart(() => ftp.uploadToFTP(uploadPath + @"/" + Path.GetFileName(file), file));
     thread[loop] = new Thread(threadstart[loop]);
     thread[loop].Start();

     if (loop == 9)
     {
         loop = 0;
     }                          
}

将文件传递到各自的线程正在运行。我的问题是线程的开始是重叠的。

一个例外的例子是当线程 1 运行时,一个文件被传递给它。它返回一个错误,因为线程 1 尚未成功完成,然后将一个新参数传递给它。其他线程也是如此。

实现这一点的最佳方法是什么?

我们将不胜感激任何反馈。谢谢! :)

【问题讨论】:

  • 您可以使用 TPL 并让 SynchronisationContext 处理您可以创建的线程数量......
  • ... 或 TaskSchedular ....
  • 由于这是一个与网络相关的问题,您应该至少使用async/await 来实现 IOCP groovyness。比启动只会阻塞网络操作的线程要好得多。您甚至可能需要考虑 TPL 数据流,至少在您的网络带宽变平之前
  • 我会为此选择 Parallel.ForEach 或 Parallel.For。
  • 如果变量相关,请考虑将它们连接到一个类,而不是创建两个数组。 (ThreadStart[] threadstart / Thread[] thread)

标签: c# multithreading upload ftp


【解决方案1】:

使用 async-await 并将一组文件传入其中:

private static async void TestFtpAsync(string userName, string password, string ftpBaseUri,
      IEnumerable<string> fileNames)
    {
      var tasks = new List<Task<byte[]>>();
      foreach (var fileInfo in fileNames.Select(fileName => new FileInfo(fileName)))
      {
        using (var webClient = new WebClient())
        {
          webClient.Credentials = new NetworkCredential(userName, password);
          tasks.Add(webClient.UploadFileTaskAsync(ftpBaseUri + fileInfo.Name, fileInfo.FullName));
        }
      }
      Console.WriteLine("Uploading...");
      foreach (var task in tasks)
      {
        try
        {
          await task;
          Console.WriteLine("Success");
        }
        catch (Exception ex)
        {
          Console.WriteLine(ex.ToString());
        }
      }
    }

然后这样称呼它:

  const string userName = "username";
  const string password = "password";
  const string ftpBaseUri = "ftp://192.168.1.1/";
  var fileNames = new[] { @"d:\file0.txt", @"d:\file1.txt", @"d:\file2.txt" };
  TestFtpAsync(userName, password, ftpBaseUri, fileNames);

【讨论】:

    【解决方案2】:

    为什么要这么辛苦? .net 已经有一个名为 ThreadPool 的类。 你可以使用它,它自己管理线程。 您的代码将是这样的:

     static void DoSomething(object n)
        {
            Console.WriteLine(n);
            Thread.Sleep(10);
        }
    
        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(20, 10);
            for (int x = 0; x < 30; x++)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomething), x);
            }
            Console.Read();
        }
    

    【讨论】:

    • 使用ThreadPool 的唯一缺点是,您必须跟踪所有作业以确定它们何时都准备就绪。您可以使用 Parallel.ForEach 来代替它。
    • 是的,为什么要这样做? Parallel.ForEach 是一个更好的解决方案。