在C#中使用SFTP时的线程最佳实践

时间:2010-04-13 15:58:52

标签: c# multithreading sftp

确定,

这是这些“概念性问题”中的一个,但我希望我能在正确的方向上找到一些指示。首先是期望的场景:

  • 我想查询SFTP服务器以获取目录和文件列表
  • 我想同时上传或下载文件

使用Tamir.SharpSsh提供的SFTP类,这两件事情都非常简单,但如果我只使用一个线程,那就有点慢了。特别是对子目录的递归会得到非常“UI阻塞”,因为我们讨论的是10.000个目录。

我的基本方法很简单,创建某种“池”,我保持10个开放的SFTP连接。然后查询第一个工作人员以获取目录列表。如果获得此列表,则发送下一个免费工作人员(例如1-10,第一个也可以再次免费)以获取子目录详细信息。一旦有工人自由,就把他送到子包装。等等...

我知道ThreadPool,简单的线程并做了一些测试。令我困惑的是以下内容:我基本上需要......

  • 我创建的主题列表,例如10
  • 将所有主题连接到服务器
  • 如果连接断开,请创建一个新的thread / sftp客户端
  • 如果有工作要做,请选择第一个免费主题并处理工作

我目前不确定实施细节,尤其是“要做的工作”和“维护线程列表”部分。

这是一个好主意:

  • 将工作包含在包含作业说明(路径)和回调
  • 的对象中
  • 将线程发送到无限循环,等待100ms等待工作
  • 如果SFTP死了,要么重新启动它,要么终止整个线程并创建一个新线程
  • 如何封装这个,我自己编写“10ThreadsManager”还是有一些

好的,到目前为止......

不过,我还可以使用PRISM事件和命令,但我认为这个问题是无关紧要的。也许EventModel表示“工作包”的完成处理......

感谢任何想法,评论家...... 克里斯

3 个答案:

答案 0 :(得分:2)

一堆小调:

如果你正在使用一些内部使用ThreadPool的.NET API,那么就不能进行无限等待,因为操作系统拥有来自ThreadPool的线程,这些线程意味着“短暂”使用然后返回到操作系统,这是最强大的行为。当然,操作系统可以根据需要增加线程池,如果由于长时间运行处理而最终占用它们,但更好的设计是避免这种行为。

如果你在XP上运行,你可能还想避免使用ThreadPool(操作系统级别,因此是.NET),因为它在Vista中被修复/重新签名,之后,XP版本被认为不太健壮。

如果您确实使用了ThreadPool,那么您最终会将异步工作排队,因为它已经在等待工作了。

编写自己的ThreadManager非常容易,你可以在其上找到很多例子,但是一如既往,这类事情应尽可能简单。

对于你的第三个要点,最好恢复SFTP连接,而不是终止你的整个线程。如果你杀掉一个线程(假设你的ThreadManager可以处理它,永远不会从操作系统ThreadPool中删除线程),而不是首先必须将未处理的作业返回到某个队列,感觉就像工作太多了。

答案 1 :(得分:0)

另一种方法是使用WeOnlyDo(http://www.weonlydo.com/FtpDLX.NET/ftp.sftp.ftps.ssl.net.component.asp)中的FtpDlx。它只有229美元,是完全管理的.Net 2.0库。它包括递归下载目录的方法。它使用事件来指示进度和错误,允许您跳过文件,重定向您编写它们的位置,以及检查和忽略错误。它很健壮,可以像宣传的那样工作;我们在多线程生产代码中使用它。

您可以在单独的线程中运行它,并使用事件来更新您的UI而不会阻塞。您甚至可能会发现执行下载的一个线程充分利用了您的带宽,但它可以在单独的线程中使用单独的连接正常工作。我建议您创建自己的线程而不是使用ThreadPool,因为您将要使用回调,并且线程往往会长时间运行。

答案 2 :(得分:0)

使用SFTP和.NET与tamir.ssh和multithead你可以这样做:

  • 创建一个主实例,它将启动Thread。

Task.Factory.StartNew(()=> new Main()。run());

  • 1.1从SFT下载文件

  • 1.2从SFTP上传文件

MAIN课程

公共课主     {         任务[] _tasks = null;

   public void run()
    {
       _tasks = new Task[2]; 

      var task = new Task(() => Download(),TaskCreationOptions.LongRunning);
                task.Start();
                _tasks[0] = task;
      var task = new Task(() => Download(),TaskCreationOptions.LongRunning);
                task.Start();
                _tasks[1] = task;

      Task.WaitAny(_tasks); 
    }

   private void Upload()
   {  Sftp _Sftp = null;
      while (true)
      {
        filesToUpload = GetFiles(Sourcepath);

           Parallel.ForEach(filesToUpload, _fileData =>
           { 
               if(!_Sftp.Insconnected)
                  {
                   _Sftp = new Sftp(Host, User,Password);
                     _Sftp.Connect();
                  }

                if(_Sftp.Connected)
                  _Sftp.Put(_fileData.Path, DestinyPath);
           });         

      }

   }

   public void Download()
    {
         Sftp _Sftp = null; 
        While(true)
        {
           if(!_Sftp.Connected)
                  {
                   _Sftp = new Sftp(Host, User,Password);
                     _Sftp.Connect();
                   }

                if(_Sftp.Connected)

{

ArrayList fileToDownload = _Sftp.GetFileList(_Instance.Sourcepath);

 Parallel.ForEach(fileToDownload, _fileData =>
      {
        _Sftp.Get(Sourcepath + "/" + _fileData.Name,DestinyPath);
      });    
       }       

    }

}